mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-22 02:28:46 +01:00
Compare commits
11 Commits
commit-ser
...
v1.8.0-rc1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
868516f0bd | ||
|
|
e50c43fb3f | ||
|
|
9b6a0dc3cd | ||
|
|
1f63e99b78 | ||
|
|
589ad5d2ac | ||
|
|
4464f99df7 | ||
|
|
65a2d9f1ff | ||
|
|
bcaabc51a1 | ||
|
|
9140fea0fb | ||
|
|
d83e0ddb56 | ||
|
|
25ca589bff |
58
CHANGELOG.md
58
CHANGELOG.md
@@ -1,5 +1,63 @@
|
||||
# Changelog
|
||||
|
||||
## v1.8.0 (Unreleased)
|
||||
|
||||
### Mono-Repository Improvements
|
||||
|
||||
Enhanced performance during manifest generation from mono-repository - the repository that represents the
|
||||
desired state of the whole cluster and contains hundreds of applications. The improved argocd-repo-server
|
||||
now able to concurrently generate manifests from the same repository and for the same commit SHA. This
|
||||
might provide 10x performance improvement of manifests generation.
|
||||
|
||||
### Annotation Based Path Detection
|
||||
|
||||
The feature that allows specifying which source repository directories influence the application manifest generation
|
||||
using the `argocd.argoproj.io/manifest-generate-paths` annotation. The annotation improves the Git webhook handler
|
||||
behavior. The webhook avoids related applications reconciliation if no related files have been changed by the Git commit
|
||||
and even allows to skip manifests generation for new commit by re-using generation manifests for the previous commit.
|
||||
|
||||
### Horizontal Controller Scaling
|
||||
|
||||
This release allows scaling the `argocd-application-controller` horizontally. This allows you to manage as many Kubernetes clusters
|
||||
as needed using a single Argo CD instance.
|
||||
|
||||
## New Core Functionality Features
|
||||
|
||||
Besides performance improvements, Argo CD got a lot of usability enhancements and new features:
|
||||
|
||||
* Namespace and CRD creation [#4354](https://github.com/argoproj/argo-cd/issues/4354)
|
||||
* Unknown fields of built-in K8S types [#1787](https://github.com/argoproj/argo-cd/issues/1787)
|
||||
* Endpoints Diffing [#1816](https://github.com/argoproj/argo-cd/issues/1816)
|
||||
* Better compatibility with Helm Hooks [#1816](https://github.com/argoproj/argo-cd/issues/1816)
|
||||
* App-of-Apps Health Assessment [#3781](https://github.com/argoproj/argo-cd/issues/3781)
|
||||
|
||||
## Global Projects
|
||||
|
||||
This release makes it easy to manage an Argo CD that has hundreds of Projects. Instead of duplicating the same organization-wide rules in all projects
|
||||
you can put such rules into one project and make this project “global” for all other projects. Rules defined in the global project are inherited by all
|
||||
other projects and therefore don’t have to be duplicated. The sample below demonstrates how you can create a global project and specify which project should
|
||||
inherit global project rules using Kubernetes labels.
|
||||
|
||||
## User Interface Improvements
|
||||
|
||||
The Argo CD user interface is an important part of a project and we keep working hard on improving the user experience. Here is an incomplete list of implemented improvements:
|
||||
|
||||
* Improved Applications Filters [#4622](https://github.com/argoproj/argo-cd/issues/4622)
|
||||
* Git tags and branches autocompletion [#4713](https://github.com/argoproj/argo-cd/issues/4713)
|
||||
* Project Details Page [#4400](https://github.com/argoproj/argo-cd/issues/4400)
|
||||
* New version information panel [#4376](https://github.com/argoproj/argo-cd/issues/4376)
|
||||
* Progress Indicators [#4411](https://github.com/argoproj/argo-cd/issues/4411)
|
||||
* External links annotations [#4380](https://github.com/argoproj/argo-cd/issues/4380) and more!
|
||||
|
||||
## Config Management Tools Enhancements
|
||||
|
||||
* OCI Based Repositories [#4018](https://github.com/argoproj/argo-cd/issues/4018)
|
||||
* Configurable Helm Versions [#4111](https://github.com/argoproj/argo-cd/issues/4111)
|
||||
|
||||
## Bug fixes and under the hood changes
|
||||
|
||||
In addition to new features and enhancements, we’ve fixed more than 50 bugs and upgraded third-party components and libraries that Argo CD relies on.
|
||||
|
||||
## v1.7.9 (2020-11-17)
|
||||
|
||||
- fix: improve commit verification tolerance (#4825)
|
||||
|
||||
@@ -85,6 +85,8 @@ const (
|
||||
DexAPIEndpoint = "/api/dex"
|
||||
// LoginEndpoint is Argo CD's shorthand login endpoint which redirects to dex's OAuth 2.0 provider's consent page
|
||||
LoginEndpoint = "/auth/login"
|
||||
// LogoutEndpoint is Argo CD's shorthand logout endpoint which invalidates OIDC session after logout
|
||||
LogoutEndpoint = "/auth/logout"
|
||||
// CallbackEndpoint is Argo CD's final callback endpoint we reach after OAuth 2.0 login flow has been completed
|
||||
CallbackEndpoint = "/auth/callback"
|
||||
// DexCallbackEndpoint is Argo CD's final callback endpoint when Dex is configured
|
||||
@@ -180,6 +182,8 @@ const (
|
||||
EnvControllerReplicas = "ARGOCD_CONTROLLER_REPLICAS"
|
||||
// EnvControllerShard is the shard number that should be handled by controller
|
||||
EnvControllerShard = "ARGOCD_CONTROLLER_SHARD"
|
||||
// EnvEnableGRPCTimeHistogramEnv enables gRPC metrics collection
|
||||
EnvEnableGRPCTimeHistogramEnv = "ARGOCD_ENABLE_GRPC_TIME_HISTOGRAM"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -36,6 +36,8 @@ and might fail. To avoid failed syncs use `ARGOCD_GIT_ATTEMPTS_COUNT` environmen
|
||||
|
||||
* `argocd_git_request_total` - Number of git requests. The metric provides two tags: `repo` - Git repo URL; `request_type` - `ls-remote` or `fetch`.
|
||||
|
||||
* `ARGOCD_ENABLE_GRPC_TIME_HISTOGRAM` (v1.8+) - environment variable that enables collecting RPC performance metrics. Enable it if you need to troubleshoot performance issue. Note: metric is expensive to both query and store!
|
||||
|
||||
### argocd-application-controller
|
||||
|
||||
**settings:**
|
||||
@@ -82,6 +84,8 @@ spec:
|
||||
value: "2"
|
||||
```
|
||||
|
||||
* `ARGOCD_ENABLE_GRPC_TIME_HISTOGRAM` (v1.8+)- environment variable that enables collecting RPC performance metrics. Enable it if you need to troubleshoot performance issue. Note: metric is expensive to both query and store!
|
||||
|
||||
**metrics**
|
||||
|
||||
* `argocd_app_reconcile` - reports application reconciliation duration. Can be used to build reconciliation duration heat map to get high-level reconciliation performance picture.
|
||||
@@ -92,6 +96,11 @@ non-preferred version and causes performance issues.
|
||||
|
||||
The `argocd-server` is stateless and probably least likely to cause issues. You might consider increasing number of replicas to 3 or more to ensure there is no downtime during upgrades.
|
||||
|
||||
**settings:**
|
||||
|
||||
* The `ARGOCD_GRPC_MAX_SIZE_MB` environment variable allows specifying the max size of the server response message in megabytes.
|
||||
The default value is 200. You might need to increase for an Argo CD instance that manages 3000+ applications.
|
||||
|
||||
### argocd-dex-server, argocd-redis
|
||||
|
||||
The `argocd-dex-server` uses an in-memory database, and two or more instances would have inconsistent data. `argocd-redis` is pre-configured with the understanding of only three total redis servers/sentinels.
|
||||
|
||||
47
docs/operator-manual/upgrading/1.7-1.8.md
Normal file
47
docs/operator-manual/upgrading/1.7-1.8.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# v1.7 to 1.8
|
||||
|
||||
## The argocd-application-controller converted to StatefulSet
|
||||
|
||||
The `argocd-application-controller` has been converted to StatefulSet. That means you need to manually delete `argocd-application-controller` Deployment after upgrading.
|
||||
Similarly if you decided to rollback to v1.7 don't forget to delete `argocd-application-controller` StatefulSet.
|
||||
|
||||
|
||||
## Health assessement of argoproj.io/Application CRD has been removed
|
||||
|
||||
The health assessement of `argoproj.io/Application` CRD has been removed (see [#3781](https://github.com/argoproj/argo-cd/issues/3781) for more information).
|
||||
You might need to restore it if you are using app-of-apps pattern and orchestrating syncronization using sync waves. Add the following resource customization in
|
||||
`argocd-cm` ConfigMap:
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: argocd-cm
|
||||
namespace: argocd
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-cm
|
||||
app.kubernetes.io/part-of: argocd
|
||||
data:
|
||||
resource.customizations: |
|
||||
argoproj.io/Application:
|
||||
health.lua: |
|
||||
hs = {}
|
||||
hs.status = "Healthy"
|
||||
hs.message = ""
|
||||
if obj.status ~= nil then
|
||||
if obj.status.health ~= nil then
|
||||
hs.status = obj.status.health.status
|
||||
hs.message = obj.status.health.message
|
||||
end
|
||||
end
|
||||
return hs
|
||||
```
|
||||
|
||||
## gRPC metrics are disabled by default
|
||||
|
||||
The gRPC metrics are not exposed by default by `argocd-server` and `argocd-repo-server` anymore. These metrics appear
|
||||
to be too expensive so we've decided to disable them by default. Metrics can be enabled using
|
||||
`ARGOCD_ENABLE_GRPC_TIME_HISTOGRAM=true` environment variable.
|
||||
|
||||
From here on you can follow the [regular upgrade process](./overview.md).
|
||||
@@ -37,6 +37,7 @@ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/<v
|
||||
|
||||
<hr/>
|
||||
|
||||
* [v1.7 to v1.8](./1.7-1.8.md)
|
||||
* [v1.6 to v1.7](./1.6-1.7.md)
|
||||
* [v1.5 to v1.6](./1.5-1.6.md)
|
||||
* [v1.4 to v1.5](./1.4-1.5.md)
|
||||
|
||||
@@ -263,3 +263,29 @@ For a simple case this can be:
|
||||
oidc.config: |
|
||||
requestedIDTokenClaims: {"groups": {"essential": true}}
|
||||
```
|
||||
### Configuring a custom logout URL for your OIDC provider
|
||||
|
||||
Optionally, if your OIDC provider exposes a logout API and you wish to configure a custom logout URL for the purposes of invalidating
|
||||
any active session post logout, you can do so by specifying it as follows:
|
||||
|
||||
```yaml
|
||||
oidc.config: |
|
||||
name: example-OIDC-provider
|
||||
issuer: https://example-OIDC-provider.com
|
||||
clientID: xxxxxxxxx
|
||||
clientSecret: xxxxxxxxx
|
||||
requestedScopes: ["openid", "profile", "email", "groups"]
|
||||
requestedIDTokenClaims: {"groups": {"essential": true}}
|
||||
logoutURL: https://example-OIDC-provider.com/logout?id_token_hint={{token}}
|
||||
```
|
||||
By default, this would take the user to their OIDC provider's login page after logout. If you also wish to redirect the user back to Argo CD after logout, you can specify the logout URL as follows:
|
||||
|
||||
```yaml
|
||||
...
|
||||
logoutURL: https://example-OIDC-provider.com/logout?id_token_hint={{token}}&post_logout_redirect_uri={{logoutRedirectURL}}
|
||||
```
|
||||
|
||||
You are not required to specify a logoutRedirectURL as this is automatically generated by ArgoCD as your base ArgoCD url + Rootpath
|
||||
|
||||
!!! note
|
||||
The post logout redirect URI may need to be whitelisted against your OIDC provider's client settings for ArgoCD.
|
||||
2
go.mod
2
go.mod
@@ -7,7 +7,7 @@ require (
|
||||
github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d
|
||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 // indirect
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible
|
||||
github.com/argoproj/gitops-engine v0.1.3-0.20201113084616-069a5e64fb79
|
||||
github.com/argoproj/gitops-engine v0.2.1
|
||||
github.com/argoproj/pkg v0.2.0
|
||||
github.com/bombsimon/logrusr v1.0.0
|
||||
github.com/casbin/casbin v1.9.1
|
||||
|
||||
4
go.sum
4
go.sum
@@ -84,8 +84,8 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
|
||||
github.com/argoproj/gitops-engine v0.1.3-0.20201113084616-069a5e64fb79 h1:+VXKYb/FNTag/vnSoH9/Pey/kXK/jBKDyOtwRfnmMMw=
|
||||
github.com/argoproj/gitops-engine v0.1.3-0.20201113084616-069a5e64fb79/go.mod h1:OxXp8YaT73rw9gEBnGBWg55af80nkV/uIjWCbJu1Nw0=
|
||||
github.com/argoproj/gitops-engine v0.2.1 h1:iXmTCCM0m2u/YVLMhJatU21awuAscGiirscn13rYQtE=
|
||||
github.com/argoproj/gitops-engine v0.2.1/go.mod h1:OxXp8YaT73rw9gEBnGBWg55af80nkV/uIjWCbJu1Nw0=
|
||||
github.com/argoproj/pkg v0.2.0 h1:ETgC600kr8WcAi3MEVY5sA1H7H/u1/IysYOobwsZ8No=
|
||||
github.com/argoproj/pkg v0.2.0/go.mod h1:F4TZgInLUEjzsWFB/BTJBsewoEy0ucnKSq6vmQiD/yc=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
|
||||
@@ -42,3 +42,18 @@ spec:
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
serviceAccountName: argocd-application-controller
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 100
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-application-controller
|
||||
topologyKey: kubernetes.io/hostname
|
||||
- weight: 5
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
|
||||
@@ -39,3 +39,12 @@ spec:
|
||||
volumes:
|
||||
- emptyDir: {}
|
||||
name: static-files
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 5
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
|
||||
@@ -5,7 +5,7 @@ kind: Kustomization
|
||||
images:
|
||||
- name: argoproj/argocd
|
||||
newName: argoproj/argocd
|
||||
newTag: latest
|
||||
newTag: v1.8.0-rc1
|
||||
resources:
|
||||
- ./application-controller
|
||||
- ./dex
|
||||
|
||||
@@ -32,3 +32,18 @@ spec:
|
||||
- "no"
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 100
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-redis
|
||||
topologyKey: kubernetes.io/hostname
|
||||
- weight: 5
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
|
||||
@@ -54,3 +54,18 @@ spec:
|
||||
name: argocd-gpg-keys-cm
|
||||
- name: gpg-keyring
|
||||
emptyDir: {}
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 100
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-repo-server
|
||||
topologyKey: kubernetes.io/hostname
|
||||
- weight: 5
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
|
||||
@@ -44,3 +44,18 @@ spec:
|
||||
- name: tls-certs
|
||||
configMap:
|
||||
name: argocd-tls-certs-cm
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 100
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-server
|
||||
topologyKey: kubernetes.io/hostname
|
||||
- weight: 5
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
|
||||
@@ -11,7 +11,7 @@ patchesStrategicMerge:
|
||||
images:
|
||||
- name: argoproj/argocd
|
||||
newName: argoproj/argocd
|
||||
newTag: latest
|
||||
newTag: v1.8.0-rc1
|
||||
resources:
|
||||
- ../../base/application-controller
|
||||
- ../../base/dex
|
||||
|
||||
@@ -2835,6 +2835,15 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-dex-server
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- command:
|
||||
- /shared/argocd-util
|
||||
@@ -2855,7 +2864,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.0-rc1
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -2993,7 +3002,7 @@ spec:
|
||||
- argocd-repo-server
|
||||
- --redis
|
||||
- argocd-redis-ha-haproxy:6379
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.0-rc1
|
||||
imagePullPolicy: Always
|
||||
name: argocd-repo-server
|
||||
ports:
|
||||
@@ -3067,7 +3076,7 @@ spec:
|
||||
env:
|
||||
- name: ARGOCD_API_SERVER_REPLICAS
|
||||
value: "2"
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.0-rc1
|
||||
imagePullPolicy: Always
|
||||
name: argocd-server
|
||||
ports:
|
||||
@@ -3113,6 +3122,21 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-application-controller
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-application-controller
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- command:
|
||||
- argocd-application-controller
|
||||
@@ -3122,7 +3146,7 @@ spec:
|
||||
- "10"
|
||||
- --redis
|
||||
- argocd-redis-ha-haproxy:6379
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.0-rc1
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -2750,6 +2750,15 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-dex-server
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- command:
|
||||
- /shared/argocd-util
|
||||
@@ -2770,7 +2779,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.0-rc1
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -2908,7 +2917,7 @@ spec:
|
||||
- argocd-repo-server
|
||||
- --redis
|
||||
- argocd-redis-ha-haproxy:6379
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.0-rc1
|
||||
imagePullPolicy: Always
|
||||
name: argocd-repo-server
|
||||
ports:
|
||||
@@ -2982,7 +2991,7 @@ spec:
|
||||
env:
|
||||
- name: ARGOCD_API_SERVER_REPLICAS
|
||||
value: "2"
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.0-rc1
|
||||
imagePullPolicy: Always
|
||||
name: argocd-server
|
||||
ports:
|
||||
@@ -3028,6 +3037,21 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-application-controller
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-application-controller
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- command:
|
||||
- argocd-application-controller
|
||||
@@ -3037,7 +3061,7 @@ spec:
|
||||
- "10"
|
||||
- --redis
|
||||
- argocd-redis-ha-haproxy:6379
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.0-rc1
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -2424,6 +2424,15 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-dex-server
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- command:
|
||||
- /shared/argocd-util
|
||||
@@ -2444,7 +2453,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.0-rc1
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -2472,6 +2481,21 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-redis
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-redis
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- args:
|
||||
- --save
|
||||
@@ -2507,6 +2531,21 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-repo-server
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-repo-server
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
automountServiceAccountToken: false
|
||||
containers:
|
||||
- command:
|
||||
@@ -2514,7 +2553,7 @@ spec:
|
||||
- argocd-repo-server
|
||||
- --redis
|
||||
- argocd-redis:6379
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.0-rc1
|
||||
imagePullPolicy: Always
|
||||
name: argocd-repo-server
|
||||
ports:
|
||||
@@ -2564,12 +2603,27 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-server
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-server
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- command:
|
||||
- argocd-server
|
||||
- --staticassets
|
||||
- /shared/app
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.0-rc1
|
||||
imagePullPolicy: Always
|
||||
name: argocd-server
|
||||
ports:
|
||||
@@ -2616,6 +2670,21 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-application-controller
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-application-controller
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- command:
|
||||
- argocd-application-controller
|
||||
@@ -2623,7 +2692,7 @@ spec:
|
||||
- "20"
|
||||
- --operation-processors
|
||||
- "10"
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.0-rc1
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -2339,6 +2339,15 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-dex-server
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- command:
|
||||
- /shared/argocd-util
|
||||
@@ -2359,7 +2368,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.0-rc1
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -2387,6 +2396,21 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-redis
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-redis
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- args:
|
||||
- --save
|
||||
@@ -2422,6 +2446,21 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-repo-server
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-repo-server
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
automountServiceAccountToken: false
|
||||
containers:
|
||||
- command:
|
||||
@@ -2429,7 +2468,7 @@ spec:
|
||||
- argocd-repo-server
|
||||
- --redis
|
||||
- argocd-redis:6379
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.0-rc1
|
||||
imagePullPolicy: Always
|
||||
name: argocd-repo-server
|
||||
ports:
|
||||
@@ -2479,12 +2518,27 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-server
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-server
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- command:
|
||||
- argocd-server
|
||||
- --staticassets
|
||||
- /shared/app
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.0-rc1
|
||||
imagePullPolicy: Always
|
||||
name: argocd-server
|
||||
ports:
|
||||
@@ -2531,6 +2585,21 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-application-controller
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-application-controller
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- command:
|
||||
- argocd-application-controller
|
||||
@@ -2538,7 +2607,7 @@ spec:
|
||||
- "20"
|
||||
- --operation-processors
|
||||
- "10"
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.0-rc1
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -39,6 +40,7 @@ import (
|
||||
versionpkg "github.com/argoproj/argo-cd/pkg/apiclient/version"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/env"
|
||||
grpc_util "github.com/argoproj/argo-cd/util/grpc"
|
||||
argoio "github.com/argoproj/argo-cd/util/io"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
@@ -53,8 +55,13 @@ const (
|
||||
EnvArgoCDServer = "ARGOCD_SERVER"
|
||||
// EnvArgoCDAuthToken is the environment variable to look for an Argo CD auth token
|
||||
EnvArgoCDAuthToken = "ARGOCD_AUTH_TOKEN"
|
||||
// EnvArgoCDgRPCMaxSizeMB is the environment variable to look for a max gRPC message size
|
||||
EnvArgoCDgRPCMaxSizeMB = "ARGOCD_GRPC_MAX_SIZE_MB"
|
||||
)
|
||||
|
||||
var (
|
||||
// MaxGRPCMessageSize contains max grpc message size
|
||||
MaxGRPCMessageSize = 100 * 1024 * 1024
|
||||
MaxGRPCMessageSize = env.ParseNumFromEnv(EnvArgoCDgRPCMaxSizeMB, 200, 0, math.MaxInt32) * 1024 * 1024
|
||||
)
|
||||
|
||||
// Client defines an interface for interaction with an Argo CD server.
|
||||
|
||||
@@ -2,6 +2,9 @@ package reposerver
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"os"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
|
||||
versionpkg "github.com/argoproj/argo-cd/pkg/apiclient/version"
|
||||
"github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
@@ -49,7 +52,9 @@ func NewServer(metricsServer *metrics.MetricsServer, cache *reposervercache.Cach
|
||||
tlsConfig := &tls.Config{Certificates: []tls.Certificate{*cert}}
|
||||
tlsConfCustomizer(tlsConfig)
|
||||
|
||||
grpc_prometheus.EnableHandlingTimeHistogram()
|
||||
if os.Getenv(common.EnvEnableGRPCTimeHistogramEnv) == "true" {
|
||||
grpc_prometheus.EnableHandlingTimeHistogram()
|
||||
}
|
||||
|
||||
serverLog := log.NewEntry(log.StandardLogger())
|
||||
streamInterceptors := []grpc.StreamServerInterceptor{grpc_logrus.StreamServerInterceptor(serverLog), grpc_prometheus.StreamServerInterceptor, grpc_util.PanicLoggerStreamServerInterceptor(serverLog)}
|
||||
|
||||
@@ -576,11 +576,12 @@ func (s *Server) Patch(ctx context.Context, q *application.ApplicationPatchReque
|
||||
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("Patch type '%s' is not supported", q.PatchType))
|
||||
}
|
||||
|
||||
err = json.Unmarshal(patchApp, &app)
|
||||
newApp := &v1alpha1.Application{}
|
||||
err = json.Unmarshal(patchApp, newApp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.validateAndUpdateApp(ctx, app, false, true)
|
||||
return s.validateAndUpdateApp(ctx, newApp, false, true)
|
||||
}
|
||||
|
||||
// Delete removes an application and all associated resources
|
||||
@@ -747,12 +748,12 @@ func (s *Server) validateAndNormalizeApp(ctx context.Context, app *appv1.Applica
|
||||
return err
|
||||
}
|
||||
|
||||
if err := argo.ValidateDestination(ctx, &app.Spec.Destination, s.db); err != nil {
|
||||
return status.Errorf(codes.InvalidArgument, "application destination spec is invalid: %s", err.Error())
|
||||
}
|
||||
|
||||
var conditions []appv1.ApplicationCondition
|
||||
if validate {
|
||||
if err := argo.ValidateDestination(ctx, &app.Spec.Destination, s.db); err != nil {
|
||||
return status.Errorf(codes.InvalidArgument, "application destination spec is invalid: %s", err.Error())
|
||||
}
|
||||
|
||||
conditions, err = argo.ValidateRepo(ctx, app, s.repoClientset, s.db, kustomizeOptions, plugins, s.kubectl)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -215,6 +215,26 @@ spec:
|
||||
name: fake-cluster
|
||||
`
|
||||
|
||||
const fakeAppWithAnnotations = `
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: test-app
|
||||
namespace: default
|
||||
annotations:
|
||||
test.annotation: test
|
||||
spec:
|
||||
source:
|
||||
path: some/path
|
||||
repoURL: https://github.com/argoproj/argocd-example-apps.git
|
||||
targetRevision: HEAD
|
||||
ksonnet:
|
||||
environment: default
|
||||
destination:
|
||||
namespace: ` + test.FakeDestNamespace + `
|
||||
server: https://cluster-api.com
|
||||
`
|
||||
|
||||
func newTestAppWithDestName(opts ...func(app *appsv1.Application)) *appsv1.Application {
|
||||
return createTestApp(fakeAppWithDestName, opts...)
|
||||
}
|
||||
@@ -223,6 +243,10 @@ func newTestApp(opts ...func(app *appsv1.Application)) *appsv1.Application {
|
||||
return createTestApp(fakeApp, opts...)
|
||||
}
|
||||
|
||||
func newTestAppWithAnnotations(opts ...func(app *appsv1.Application)) *appsv1.Application {
|
||||
return createTestApp(fakeAppWithAnnotations, opts...)
|
||||
}
|
||||
|
||||
func createTestApp(testApp string, opts ...func(app *appsv1.Application)) *appsv1.Application {
|
||||
var app appsv1.Application
|
||||
err := yaml.Unmarshal([]byte(testApp), &app)
|
||||
@@ -499,7 +523,7 @@ p, admin, applications, update, my-proj/test-app, allow
|
||||
}
|
||||
|
||||
func TestAppJsonPatch(t *testing.T) {
|
||||
testApp := newTestApp()
|
||||
testApp := newTestAppWithAnnotations()
|
||||
ctx := context.Background()
|
||||
// nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, "claims", &jwt.StandardClaims{Subject: "admin"})
|
||||
@@ -517,6 +541,10 @@ func TestAppJsonPatch(t *testing.T) {
|
||||
app, err = appServer.Patch(ctx, &application.ApplicationPatchRequest{Name: &testApp.Name, Patch: `[{"op": "replace", "path": "/spec/source/path", "value": "foo"}]`})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "foo", app.Spec.Source.Path)
|
||||
|
||||
app, err = appServer.Patch(ctx, &application.ApplicationPatchRequest{Name: &testApp.Name, Patch: `[{"op": "remove", "path": "/metadata/annotations/test.annotation"}]`})
|
||||
assert.NoError(t, err)
|
||||
assert.NotContains(t, app.Annotations, "test.annotation")
|
||||
}
|
||||
|
||||
func TestAppMergePatch(t *testing.T) {
|
||||
|
||||
97
server/logout/logout.go
Normal file
97
server/logout/logout.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package logout
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/util/session"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
|
||||
jwtutil "github.com/argoproj/argo-cd/util/jwt"
|
||||
)
|
||||
|
||||
//NewHandler creates handler serving to do api/logout endpoint
|
||||
func NewHandler(appClientset versioned.Interface, settingsMrg *settings.SettingsManager, sessionMgr *session.SessionManager, rootPath, namespace string) *Handler {
|
||||
return &Handler{
|
||||
appClientset: appClientset,
|
||||
namespace: namespace,
|
||||
settingsMgr: settingsMrg,
|
||||
rootPath: rootPath,
|
||||
verifyToken: sessionMgr.VerifyToken,
|
||||
}
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
namespace string
|
||||
appClientset versioned.Interface
|
||||
settingsMgr *settings.SettingsManager
|
||||
rootPath string
|
||||
verifyToken func(tokenString string) (jwt.Claims, error)
|
||||
}
|
||||
|
||||
var (
|
||||
tokenPattern = regexp.MustCompile(`{{token}}`)
|
||||
logoutRedirectURLPattern = regexp.MustCompile(`{{logoutRedirectURL}}`)
|
||||
)
|
||||
|
||||
func constructLogoutURL(logoutURL, token, logoutRedirectURL string) string {
|
||||
constructedLogoutURL := tokenPattern.ReplaceAllString(logoutURL, token)
|
||||
return logoutRedirectURLPattern.ReplaceAllString(constructedLogoutURL, logoutRedirectURL)
|
||||
}
|
||||
|
||||
// ServeHTTP is the logout handler for ArgoCD and constructs OIDC logout URL and redirects to it for OIDC issued sessions,
|
||||
// and redirects user to '/login' for argocd issued sessions
|
||||
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
var tokenString string
|
||||
var oidcConfig *settings.OIDCConfig
|
||||
|
||||
argoCDSettings, err := h.settingsMgr.GetSettings()
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
http.Error(w, "Failed to retrieve argoCD settings: "+fmt.Sprintf("%s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
logoutRedirectURL := strings.TrimRight(strings.TrimLeft(argoCDSettings.URL, "/"), "/") + strings.TrimRight(strings.TrimLeft(h.rootPath, "/"), "/")
|
||||
|
||||
argocdCookie, err := r.Cookie(common.AuthCookieName)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
http.Error(w, "Failed to retrieve ArgoCD auth token: "+fmt.Sprintf("%s", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
tokenString = argocdCookie.Value
|
||||
|
||||
argocdCookie.Value = ""
|
||||
argocdCookie.Path = fmt.Sprintf("/%s", strings.TrimRight(strings.TrimLeft(h.rootPath, "/"), "/"))
|
||||
w.Header().Set("Set-Cookie", argocdCookie.String())
|
||||
|
||||
claims, err := h.verifyToken(tokenString)
|
||||
if err != nil {
|
||||
http.Redirect(w, r, logoutRedirectURL, http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
mapClaims, err := jwtutil.MapClaims(claims)
|
||||
if err != nil {
|
||||
http.Redirect(w, r, logoutRedirectURL, http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
issuer := jwtutil.GetField(mapClaims, "iss")
|
||||
|
||||
if argoCDSettings.OIDCConfig() == nil || argoCDSettings.OIDCConfig().LogoutURL == "" || issuer == session.SessionManagerClaimsIssuer {
|
||||
http.Redirect(w, r, logoutRedirectURL, http.StatusSeeOther)
|
||||
} else {
|
||||
oidcConfig = argoCDSettings.OIDCConfig()
|
||||
logoutURL := constructLogoutURL(oidcConfig.LogoutURL, tokenString, logoutRedirectURL)
|
||||
http.Redirect(w, r, logoutURL, http.StatusSeeOther)
|
||||
}
|
||||
}
|
||||
291
server/logout/logout_test.go
Normal file
291
server/logout/logout_test.go
Normal file
@@ -0,0 +1,291 @@
|
||||
package logout
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/util/session"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned/fake"
|
||||
)
|
||||
|
||||
var (
|
||||
validJWTPattern = regexp.MustCompile(`[a-zA-Z0-9-_]+\.[a-zA-Z0-9-_]+\.[a-zA-Z0-9-_]+`)
|
||||
baseURL = "http://localhost:4000"
|
||||
baseLogoutURL = "http://localhost:4000/logout"
|
||||
baseLogoutURLwithToken = "http://localhost:4000/logout?id_token_hint={{token}}"
|
||||
baseLogoutURLwithRedirectURL = "http://localhost:4000/logout?post_logout_redirect_uri={{logoutRedirectURL}}"
|
||||
baseLogoutURLwithTokenAndRedirectURL = "http://localhost:4000/logout?id_token_hint={{token}}&post_logout_redirect_uri={{logoutRedirectURL}}"
|
||||
invalidToken = "sample-token"
|
||||
oidcToken = "eyJraWQiOiJYQi1MM3ZFdHhYWXJLcmRSQnVEV0NwdnZsSnk3SEJVb2d5N253M1U1Z1ZZIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiIwMHVqNnM1NDVyNU5peVNLcjVkNSIsIm5hbWUiOiJqZCByIiwiZW1haWwiOiJqYWlkZWVwMTdydWx6QGdtYWlsLmNvbSIsInZlciI6MSwiaXNzIjoiaHR0cHM6Ly9kZXYtNTY5NTA5OC5va3RhLmNvbSIsImF1ZCI6IjBvYWowM2FmSEtqN3laWXJwNWQ1IiwiaWF0IjoxNjA1NTcyMzU5LCJleHAiOjE2MDU1NzU5NTksImp0aSI6IklELl9ORDJxVG5iREFtc3hIZUt2U2ZHeVBqTXRicXFEQXdkdlRQTDZCTnpfR3ciLCJhbXIiOlsicHdkIl0sImlkcCI6IjAwb2lnaGZmdkpRTDYzWjhoNWQ1IiwicHJlZmVycmVkX3VzZXJuYW1lIjoiamFpZGVlcDE3cnVsekBnbWFpbC5jb20iLCJhdXRoX3RpbWUiOjE2MDU1NzIzNTcsImF0X2hhc2giOiJqZVEwRml2ak9nNGI2TUpXRDIxOWxnIn0.GHkqwXgW-lrAhJdypW7SVjW0YdNLFQiRL8iwgT6DHJxP9Nb0OtkH2NKcBYAA5N6bTPLRQUHgYwWcgm5zSXmvqa7ciIgPF3tiQI8UmJA9VFRRDR-x9ExX15nskCbXfiQ67MriLslUrQUyzSCfUrSjXKwnDxbKGQncrtmRsh5asfCzJFb9excn311W9HKbT3KA0Ot7eOMnVS6V7SGfXxnKs6szcXIEMa_FhB4zDAVLr-dnxvSG_uuWcHrAkLTUVhHbdQQXF7hXIEfyr5lkMJN-drjdz-bn40GaYulEmUvO1bjcL9toCVQ3Ismypyr0b8phj4w3uRsLDZQxTxK7jAXlyQ"
|
||||
nonOidcToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2MDU1NzQyMTIsImlzcyI6ImFyZ29jZCIsIm5iZiI6MTYwNTU3NDIxMiwic3ViIjoiYWRtaW4ifQ.zDJ4piwWnwsHON-oPusHMXWINlnrRDTQykYogT7afeE"
|
||||
expectedNonOIDCLogoutURL = "http://localhost:4000"
|
||||
expectedOIDCLogoutURL = "https://dev-5695098.okta.com/oauth2/v1/logout?id_token_hint=" + oidcToken + "&post_logout_redirect_uri=" + baseURL
|
||||
)
|
||||
|
||||
func TestConstructLogoutURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
logoutURL string
|
||||
token string
|
||||
logoutRedirectURL string
|
||||
expectedLogoutURL string
|
||||
}{
|
||||
{
|
||||
name: "Case: No additional parameters passed to logout URL",
|
||||
logoutURL: baseLogoutURL,
|
||||
token: oidcToken,
|
||||
logoutRedirectURL: baseURL,
|
||||
expectedLogoutURL: baseLogoutURL,
|
||||
},
|
||||
{
|
||||
name: "Case: ID token passed to logout URL",
|
||||
logoutURL: baseLogoutURLwithToken,
|
||||
token: oidcToken,
|
||||
logoutRedirectURL: baseURL,
|
||||
expectedLogoutURL: "http://localhost:4000/logout?id_token_hint=" + oidcToken,
|
||||
},
|
||||
{
|
||||
name: "Case: Redirect required",
|
||||
logoutURL: baseLogoutURLwithRedirectURL,
|
||||
token: oidcToken,
|
||||
logoutRedirectURL: baseURL,
|
||||
expectedLogoutURL: "http://localhost:4000/logout?post_logout_redirect_uri=" + baseURL,
|
||||
},
|
||||
{
|
||||
name: "Case: ID token and redirect URL passed to logout URL",
|
||||
logoutURL: baseLogoutURLwithTokenAndRedirectURL,
|
||||
token: oidcToken,
|
||||
logoutRedirectURL: baseURL,
|
||||
expectedLogoutURL: "http://localhost:4000/logout?id_token_hint=" + oidcToken + "&post_logout_redirect_uri=" + baseURL,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
constructedLogoutURL := constructLogoutURL(tt.logoutURL, tt.token, tt.logoutRedirectURL)
|
||||
assert.Equal(t, constructedLogoutURL, tt.expectedLogoutURL)
|
||||
})
|
||||
}
|
||||
}
|
||||
func TestHandlerConstructLogoutURL(t *testing.T) {
|
||||
kubeClientWithOIDCConfig := fake.NewSimpleClientset(
|
||||
&corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.ArgoCDConfigMapName,
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
},
|
||||
},
|
||||
Data: map[string]string{
|
||||
"oidc.config": "name: Okta \n" +
|
||||
"issuer: https://dev-5695098.okta.com \n" +
|
||||
"requestedScopes: [\"openid\", \"profile\", \"email\", \"groups\"] \n" +
|
||||
"requestedIDTokenClaims: {\"groups\": {\"essential\": true}} \n" +
|
||||
"logoutURL: https://dev-5695098.okta.com/oauth2/v1/logout?id_token_hint={{token}}&post_logout_redirect_uri={{logoutRedirectURL}}",
|
||||
"url": "http://localhost:4000",
|
||||
},
|
||||
},
|
||||
&corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.ArgoCDSecretName,
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"admin.password": nil,
|
||||
"server.secretkey": nil,
|
||||
},
|
||||
},
|
||||
)
|
||||
kubeClientWithOIDCConfigButNoLogoutURL := fake.NewSimpleClientset(
|
||||
&corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.ArgoCDConfigMapName,
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
},
|
||||
},
|
||||
Data: map[string]string{
|
||||
"oidc.config": "name: Okta \n" +
|
||||
"issuer: https://dev-5695098.okta.com \n" +
|
||||
"requestedScopes: [\"openid\", \"profile\", \"email\", \"groups\"] \n" +
|
||||
"requestedIDTokenClaims: {\"groups\": {\"essential\": true}} \n",
|
||||
"url": "http://localhost:4000",
|
||||
},
|
||||
},
|
||||
&corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.ArgoCDSecretName,
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"admin.password": nil,
|
||||
"server.secretkey": nil,
|
||||
},
|
||||
},
|
||||
)
|
||||
kubeClientWithoutOIDCConfig := fake.NewSimpleClientset(
|
||||
&corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.ArgoCDConfigMapName,
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
},
|
||||
},
|
||||
Data: map[string]string{
|
||||
"url": "http://localhost:4000",
|
||||
},
|
||||
},
|
||||
&corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.ArgoCDSecretName,
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"admin.password": nil,
|
||||
"server.secretkey": nil,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
settingsManagerWithOIDCConfig := settings.NewSettingsManager(context.Background(), kubeClientWithOIDCConfig, "default")
|
||||
settingsManagerWithoutOIDCConfig := settings.NewSettingsManager(context.Background(), kubeClientWithoutOIDCConfig, "default")
|
||||
settingsManagerWithOIDCConfigButNoLogoutURL := settings.NewSettingsManager(context.Background(), kubeClientWithOIDCConfigButNoLogoutURL, "default")
|
||||
|
||||
sessionManager := session.NewSessionManager(settingsManagerWithOIDCConfig, "", session.NewInMemoryUserStateStorage())
|
||||
|
||||
oidcHandler := NewHandler(appclientset.NewSimpleClientset(), settingsManagerWithOIDCConfig, sessionManager, "", "default")
|
||||
oidcHandler.verifyToken = func(tokenString string) (jwt.Claims, error) {
|
||||
if !validJWTPattern.MatchString(tokenString) {
|
||||
return nil, errors.New("invalid jwt")
|
||||
}
|
||||
return &jwt.StandardClaims{Issuer: "okta"}, nil
|
||||
}
|
||||
nonoidcHandler := NewHandler(appclientset.NewSimpleClientset(), settingsManagerWithoutOIDCConfig, sessionManager, "", "default")
|
||||
nonoidcHandler.verifyToken = func(tokenString string) (jwt.Claims, error) {
|
||||
if !validJWTPattern.MatchString(tokenString) {
|
||||
return nil, errors.New("invalid jwt")
|
||||
}
|
||||
return &jwt.StandardClaims{Issuer: session.SessionManagerClaimsIssuer}, nil
|
||||
}
|
||||
oidcHandlerWithoutLogoutURL := NewHandler(appclientset.NewSimpleClientset(), settingsManagerWithOIDCConfigButNoLogoutURL, sessionManager, "", "default")
|
||||
oidcHandlerWithoutLogoutURL.verifyToken = func(tokenString string) (jwt.Claims, error) {
|
||||
if !validJWTPattern.MatchString(tokenString) {
|
||||
return nil, errors.New("invalid jwt")
|
||||
}
|
||||
return &jwt.StandardClaims{Issuer: "okta"}, nil
|
||||
}
|
||||
|
||||
oidcTokenHeader := make(map[string][]string)
|
||||
oidcTokenHeader["Cookie"] = []string{"argocd.token=" + oidcToken}
|
||||
nonOidcTokenHeader := make(map[string][]string)
|
||||
nonOidcTokenHeader["Cookie"] = []string{"argocd.token=" + nonOidcToken}
|
||||
invalidHeader := make(map[string][]string)
|
||||
invalidHeader["Cookie"] = []string{"argocd.token=" + invalidToken}
|
||||
|
||||
oidcRequest, err := http.NewRequest("GET", "http://localhost:4000/api/logout", nil)
|
||||
assert.NoError(t, err)
|
||||
oidcRequest.Header = oidcTokenHeader
|
||||
nonoidcRequest, err := http.NewRequest("GET", "http://localhost:4000/api/logout", nil)
|
||||
assert.NoError(t, err)
|
||||
nonoidcRequest.Header = nonOidcTokenHeader
|
||||
assert.NoError(t, err)
|
||||
requestWithInvalidToken, err := http.NewRequest("GET", "http://localhost:4000/api/logout", nil)
|
||||
assert.NoError(t, err)
|
||||
requestWithInvalidToken.Header = invalidHeader
|
||||
invalidRequest, err := http.NewRequest("GET", "http://localhost:4000/api/logout", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
kubeClient *fake.Clientset
|
||||
handler http.Handler
|
||||
request *http.Request
|
||||
responseRecorder *httptest.ResponseRecorder
|
||||
expectedLogoutURL string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Case: OIDC logout request with valid token",
|
||||
handler: oidcHandler,
|
||||
request: oidcRequest,
|
||||
responseRecorder: httptest.NewRecorder(),
|
||||
expectedLogoutURL: expectedOIDCLogoutURL,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Case: non-OIDC logout request with valid token",
|
||||
handler: nonoidcHandler,
|
||||
request: nonoidcRequest,
|
||||
responseRecorder: httptest.NewRecorder(),
|
||||
expectedLogoutURL: expectedNonOIDCLogoutURL,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Case: Logout request with invalid token",
|
||||
handler: nonoidcHandler,
|
||||
request: requestWithInvalidToken,
|
||||
responseRecorder: httptest.NewRecorder(),
|
||||
expectedLogoutURL: expectedNonOIDCLogoutURL,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Case: Logout request with missing token",
|
||||
handler: oidcHandler,
|
||||
request: invalidRequest,
|
||||
responseRecorder: httptest.NewRecorder(),
|
||||
expectedLogoutURL: expectedNonOIDCLogoutURL,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Case:OIDC Logout request with missing logout URL configuration in config map",
|
||||
handler: oidcHandlerWithoutLogoutURL,
|
||||
request: oidcRequest,
|
||||
responseRecorder: httptest.NewRecorder(),
|
||||
expectedLogoutURL: expectedNonOIDCLogoutURL,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.handler.ServeHTTP(tt.responseRecorder, tt.request)
|
||||
if status := tt.responseRecorder.Code; status != http.StatusSeeOther {
|
||||
if !tt.wantErr {
|
||||
t.Errorf(tt.responseRecorder.Body.String())
|
||||
t.Errorf("handler returned wrong status code: " + fmt.Sprintf("%d", tt.responseRecorder.Code))
|
||||
}
|
||||
} else {
|
||||
if tt.wantErr {
|
||||
t.Errorf("expected error but did not get one")
|
||||
} else {
|
||||
assert.Equal(t, tt.expectedLogoutURL, tt.responseRecorder.Result().Header["Location"][0])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -73,6 +73,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/server/certificate"
|
||||
"github.com/argoproj/argo-cd/server/cluster"
|
||||
"github.com/argoproj/argo-cd/server/gpgkey"
|
||||
"github.com/argoproj/argo-cd/server/logout"
|
||||
"github.com/argoproj/argo-cd/server/metrics"
|
||||
"github.com/argoproj/argo-cd/server/project"
|
||||
"github.com/argoproj/argo-cd/server/rbacpolicy"
|
||||
@@ -104,7 +105,6 @@ import (
|
||||
|
||||
const maxConcurrentLoginRequestsCountEnv = "ARGOCD_MAX_CONCURRENT_LOGIN_REQUESTS_COUNT"
|
||||
const replicasCountEnv = "ARGOCD_API_SERVER_REPLICAS"
|
||||
const enableGRPCTimeHistogramEnv = "ARGOCD_ENABLE_GRPC_TIME_HISTOGRAM"
|
||||
|
||||
// ErrNoSession indicates no auth token was supplied as part of a request
|
||||
var ErrNoSession = status.Errorf(codes.Unauthenticated, "no session information")
|
||||
@@ -138,7 +138,7 @@ func init() {
|
||||
if replicasCount > 0 {
|
||||
maxConcurrentLoginRequestsCount = maxConcurrentLoginRequestsCount / replicasCount
|
||||
}
|
||||
enableGRPCTimeHistogram = os.Getenv(enableGRPCTimeHistogramEnv) != "false"
|
||||
enableGRPCTimeHistogram = os.Getenv(common.EnvEnableGRPCTimeHistogramEnv) == "true"
|
||||
}
|
||||
|
||||
// ArgoCDServer is the API server for Argo CD
|
||||
@@ -624,7 +624,8 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl
|
||||
Handler: &handlerSwitcher{
|
||||
handler: mux,
|
||||
urlToHandler: map[string]http.Handler{
|
||||
"/api/badge": badge.NewHandler(a.AppClientset, a.settingsMgr, a.Namespace),
|
||||
"/api/badge": badge.NewHandler(a.AppClientset, a.settingsMgr, a.Namespace),
|
||||
common.LogoutEndpoint: logout.NewHandler(a.AppClientset, a.settingsMgr, a.sessionMgr, a.ArgoCDServerOpts.RootPath, a.Namespace),
|
||||
},
|
||||
contentTypeToHandler: map[string]http.Handler{
|
||||
"application/grpc-web+proto": grpcWebHandler,
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as React from 'react';
|
||||
import {BehaviorSubject, Observable} from 'rxjs';
|
||||
import {AppContext} from '../context';
|
||||
import {services} from '../services';
|
||||
|
||||
import requests from '../services/requests';
|
||||
const mostRecentLoggedIn = new BehaviorSubject<boolean>(false);
|
||||
|
||||
function isLoggedIn(): Observable<boolean> {
|
||||
@@ -51,9 +51,10 @@ export class Page extends React.Component<{title: string; toolbar?: Toolbar | Ob
|
||||
|
||||
private async goToLogin(logout = false) {
|
||||
if (logout) {
|
||||
await services.users.logout();
|
||||
window.location.href = requests.toAbsURL('/auth/logout');
|
||||
} else {
|
||||
this.appContext.history.push('/login');
|
||||
}
|
||||
this.appContext.history.push('/login');
|
||||
}
|
||||
|
||||
private get appContext(): AppContext {
|
||||
|
||||
@@ -97,6 +97,7 @@ type OIDCConfig struct {
|
||||
CLIClientID string `json:"cliClientID,omitempty"`
|
||||
RequestedScopes []string `json:"requestedScopes,omitempty"`
|
||||
RequestedIDTokenClaims map[string]*oidc.Claim `json:"requestedIDTokenClaims,omitempty"`
|
||||
LogoutURL string `json:"logoutURL,omitempty"`
|
||||
}
|
||||
|
||||
// DEPRECATED. Helm repository credentials are now managed using RepoCredentials
|
||||
|
||||
Reference in New Issue
Block a user