Compare commits

...

11 Commits

Author SHA1 Message Date
argo-bot
868516f0bd Bump version to 1.8.0-rc1 2020-11-25 18:03:58 +00:00
argo-bot
e50c43fb3f Bump version to 1.8.0-rc1 2020-11-25 18:03:44 +00:00
Alexander Matyushentsev
9b6a0dc3cd refactor: disable gRPC metrics by default (#4892)
Signed-off-by: Alexander Matyushentsev <Alexander_Matyushentsev@intuit.com>
2020-11-23 16:45:34 -08:00
Alexander Matyushentsev
1f63e99b78 docs: add v1.8 changelog and upgrading instructions (#4888)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-23 16:45:31 -08:00
Alexander Matyushentsev
589ad5d2ac fix: upgrade gitops-engine version. (fixes #4877) (#4890)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-23 13:02:59 -08:00
kshamajain99
4464f99df7 fix: validate empty server address for destination cluster (#4852) (#4860)
* Always set inferred destination server

Signed-off-by: kshamajain99 <kshamajain99@gmail.com>
2020-11-20 16:46:24 -08:00
Jaideep Raghunath Rao
65a2d9f1ff feat: Allow configuration of OIDC logout URL to invalidate SSO session after logout (#4452) (#4826)
feat: Allow configuration of OIDC logout URL to invalidate SSO session after logout (#4452) (#4826)

Signed-off-by: jaideepr97 <jaideep.r97@gmail.com>
2020-11-20 11:21:11 -08:00
Sho Okada
bcaabc51a1 fix: argocd app patch remove does not work (#4585)
Signed-off-by: Sho Okada <shokada3@gmail.com>
2020-11-20 11:21:07 -08:00
Alexander Matyushentsev
9140fea0fb fix: increase max grpc message size (#4869)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-20 11:20:59 -08:00
Alexander Matyushentsev
d83e0ddb56 chore: use release tag to reference gitops engine dependency (#4866)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-19 14:10:31 -08:00
Mikhail Nacharov
25ca589bff fix: Adds podAntiAffinity in base manifests (#4549) (#4599)
Signed-off-by: Mikhail Vladimirovich Nacharov <author@webnach.ru>
2020-11-19 12:08:56 -08:00
29 changed files with 869 additions and 37 deletions

View File

@@ -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 dont 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, weve 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)

View File

@@ -1 +1 @@
1.8.0
1.8.0-rc1

View File

@@ -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 (

View File

@@ -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.

View 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).

View File

@@ -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)

View File

@@ -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
View File

@@ -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
View File

@@ -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=

View File

@@ -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

View File

@@ -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

View File

@@ -5,7 +5,7 @@ kind: Kustomization
images:
- name: argoproj/argocd
newName: argoproj/argocd
newTag: latest
newTag: v1.8.0-rc1
resources:
- ./application-controller
- ./dex

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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:

View File

@@ -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:

View File

@@ -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:

View File

@@ -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.

View File

@@ -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)}

View File

@@ -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

View File

@@ -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
View 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)
}
}

View 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])
}
}
})
}
}

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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