Compare commits

..

22 Commits

Author SHA1 Message Date
argo-bot
78d749ec88 Bump version to 2.2.4 2022-02-03 20:33:05 +00:00
argo-bot
8217d70085 Bump version to 2.2.4 2022-02-03 20:32:49 +00:00
jannfis
02e61797b3 Merge pull request from GHSA-63qx-x74g-jcr7
Signed-off-by: jannfis <jann@mistrust.net>
2022-02-03 20:37:46 +01:00
jannfis
998f063a80 chore: upgrade dex to v2.30.2 (backport of #8237) (#8257)
Signed-off-by: jannfis <jann@mistrust.net>

Co-authored-by: Alexander Matyushentsev <Alexander_Matyushentsev@intuit.com>
2022-01-24 10:17:41 -08:00
argo-bot
987f6659b8 Bump version to 2.2.3 2022-01-18 17:45:46 +00:00
argo-bot
e099a6a851 Bump version to 2.2.3 2022-01-18 17:45:31 +00:00
Alexander Matyushentsev
afbd59ba63 refactor: introduce 'byClusterName' secret index to speedup cluster server URL lookup (#8133)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2022-01-17 19:25:00 -08:00
pasha-codefresh
b1e3a07d92 fix: application exist panic when execute api call (#8188)
fix: application exist panic when execute api call (#8188)

Signed-off-by: pashavictorovich <pavel@codefresh.io>
2022-01-15 09:05:19 +01:00
Ishita Sequeira
c3144c0059 fix: route health check stuck in 'Progressing' (#8170)
Signed-off-by: ishitasequeira <isequeir@redhat.com>
2022-01-14 09:03:39 +00:00
jannfis
33547f149b chore: Update to Redis 6.2.4 (#8157) (#8158)
Signed-off-by: jannfis <jann@mistrust.net>
2022-01-12 13:45:56 -08:00
Alexander Matyushentsev
06dc9aa836 docs: update roadmap document with v2.2 release changes (#8089)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2022-01-05 09:21:41 -08:00
argo-bot
03b17e0233 Bump version to 2.2.2 2022-01-01 06:18:52 +00:00
argo-bot
d5909f7168 Bump version to 2.2.2 2022-01-01 06:18:35 +00:00
pasha-codefresh
7d0d665747 fix: issue with project scoped resources (#8048)
fix: issue with project scoped resources (#8048)

Signed-off-by: pashavictorovich <pavel@codefresh.io>
2021-12-30 09:12:45 -08:00
Michael Crenshaw
834a102c09 chore: escape proj in regex (#7985)
* chore: escape proj in regex

Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>

* chore: test normal cases

Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>

* chore: typo

Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>
2021-12-30 09:12:40 -08:00
plakyda-codefresh
4bcd8cf733 fix: Default value for retry validation #8055 (#8064)
fix: Default value for retry validation #8055 (#8064)

Signed-off-by: viktorplakida <plakyda1@gmail.com>
2021-12-30 09:04:56 -08:00
pasha-codefresh
a069c602dc fix: sync window panel is crashed if resource name not contain letters (#8053)
fix: sync window panel is crashed if resource name not contain letters (#8053)

Signed-off-by: pashavictorovich <pavel@codefresh.io>
2021-12-29 11:08:52 -08:00
Alexander Matyushentsev
e309ceebac fix: upgrade github.com/argoproj/gitops-engine to v0.5.2
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2021-12-22 13:45:57 -08:00
plakyda-codefresh
4a7f0bbfd8 fix: retry disabled text (#8004)
fix: retry disabled text (#8004)

Signed-off-by: viktorplakida <plakyda1@gmail.com>
2021-12-22 11:40:30 -08:00
Niklas Steiner
28a54bf2a2 fix: Opening app details shows UI error on some apps (#8016) (#8019)
Signed-off-by: Niklas Steiner <niklas@sbg.at>
2021-12-22 11:17:50 -08:00
Alexander Matyushentsev
e209426a7e fix: correctly handle project field during partial cluster update (#7994)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2021-12-21 11:10:39 -08:00
May Zhang
06a95f86ce fix: Cluster API does not support updating labels and annotations (#7901)
Signed-off-by: May Zhang <may_zhang@intuit.com>
2021-12-21 11:10:32 -08:00
43 changed files with 758 additions and 207 deletions

View File

@@ -400,7 +400,7 @@ jobs:
run: |
docker pull quay.io/dexidp/dex:v2.25.0
docker pull argoproj/argo-cd-ci-builder:v1.0.0
docker pull redis:6.2.4-alpine
docker pull redis:6.2.6-alpine
- name: Create target directory for binaries in the build-process
run: |
mkdir -p dist

View File

@@ -1,7 +1,7 @@
controller: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller go run ./cmd/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
api-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server go run ./cmd/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} "
dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v2/cmd gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:v2.30.0 serve /dex.yaml"
redis: bash -c "if [ \"$ARGOCD_REDIS_LOCAL\" == 'true' ]; then redis-server --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; else docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} redis:6.2.4-alpine --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; fi"
dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v2/cmd gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:v2.30.2 dex serve /dex.yaml"
redis: bash -c "if [ \"$ARGOCD_REDIS_LOCAL\" == 'true' ]; then redis-server --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; else docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} redis:6.2.6-alpine --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; fi"
repo-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-/tmp/argo-e2e/app/config/plugin} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-repo-server ARGOCD_GPG_ENABLED=${ARGOCD_GPG_ENABLED:-false} go run ./cmd/main.go --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379}"
ui: sh -c 'cd ui && ${ARGOCD_E2E_YARN_CMD:-yarn} start'
git-server: test/fixture/testrepos/start-git.sh

View File

@@ -1 +1 @@
2.2.1
2.2.4

View File

@@ -1,26 +1,26 @@
# Roadmap
- [Roadmap](#roadmap)
- [v2.2](#v22)
- [Config Management Tools Integrations (proposal)](#config-management-tools-integrations-proposal)
- [Argo CD Extensions (proposal)](#argo-cd-extensions-proposal)
- [Project scoped repository and clusters (proposal)](#project-scoped-repository-and-clusters-proposal)
- [v2.3 and beyond](#v23-and-beyond)
- [Input Forms UI Refresh](#input-forms-ui-refresh)
- [Merge ApplicationSet controller into Argo CD](#merge-applicationset-controller-into-argo-cd)
- [v2.3](#v23)
- [Merge Argo CD Notifications into Argo CD](#merge-argo-cd-notifications-into-argo-cd)
- [Merge Argo CD Image Updater into Argo CD](#merge-argo-cd-image-updater-into-argo-cd)
- [Compact Resources Tree](#compact-resources-tree)
- [Input Forms UI Refresh](#input-forms-ui-refresh)
- [Compact resources tree](#compact-resources-tree)
- [Maintain difference in cluster and git values for specific fields](#maintain-difference-in-cluster-and-git-values-for-specific-fields)
- [Web Shell](#web-shell)
- [Helm values from external repo](#helm-values-from-external-repo)
- [v2.4 and beyond](#v24-and-beyond)
- [Merge ApplicationSet controller into Argo CD](#merge-applicationset-controller-into-argo-cd)
- [Merge Argo CD Image Updater into Argo CD](#merge-argo-cd-image-updater-into-argo-cd)
- [Config Management Tools Integrations UI/CLI](#config-management-tools-integrations-uicli)
- [Allow specifying parent/child relationships in config](#allow-specifying-parentchild-relationships-in-config)
- [Dependencies between applications](#dependencies-between-applications)
- [Maintain difference in cluster and git values for specific fields](#maintain-difference-in-cluster-and-git-values-for-specific-fields)
- [Multi-tenancy improvements](#multi-tenancy-improvements)
- [GitOps Engine Enhancements](#gitops-engine-enhancements)
- [Completed](#completed)
- [✅ Core Argo CD (proposal)](#core-argo-cd-aka-gitops-agent-proposal)
- [✅ Config Management Tools Integrations (proposal)](#-config-management-tools-integrations-proposal)
- [✅ Argo CD Extensions (proposal)](#-argo-cd-extensions-proposal)
- [✅ Project scoped repository and clusters (proposal)](#-project-scoped-repository-and-clusters-proposal)
- [✅ Core Argo CD (proposal)](#-core-argo-cd-proposal)
- [✅ Core Functionality Bug Fixes](#-core-functionality-bug-fixes)
- [✅ Performance](#-performance)
- [✅ ApplicationSet](#-applicationset)
@@ -30,50 +30,24 @@
- [✅ Automated Registry Monitoring](#-automated-registry-monitoring)
- [✅ Projects Enhancements](#-projects-enhancements)
## v2.2
### Config Management Tools Integrations ([proposal](https://github.com/argoproj/argo-cd/pull/5927))
The community likes the first class support of Helm, Kustomize and keeps requesting support for more tools.
Argo CD provides a mechanism to integrate with any config management tool. We need to investigate why
it is not enough and implement missing features.
### Argo CD Extensions ([proposal](https://github.com/argoproj/argo-cd/pull/6240))
Argo CD supports customizing handling of Kubernetes resources via diffing customizations,
health checks, and custom actions. The Argo CD Extensions proposal takes it to next
level and allows to deliver the resource customizations along with custom visualization in Argo CD
via Git repository.
### Project scoped repository and clusters ([proposal](https://github.com/argoproj/argo-cd/blob/master/docs/proposals/project-repos-and-clusters.md))
The feature streamlines the process of adding repositories and clusters to the project and makes it self-service.
Instead of asking an administrator to change Argo CD settings end users can perform the change independently.
## v2.3 and beyond
### Input Forms UI Refresh
Improved design of the input forms in Argo CD Web UI: https://www.figma.com/file/IIlsFqqmM5UhqMVul9fQNq/Argo-CD?node-id=0%3A1
### Merge ApplicationSet controller into Argo CD
The ApplicationSet functionality is available in Argo CD out-of-the-box ([#7351](https://github.com/argoproj/argo-cd/issues/7351)).
The Argo CD UI/CLI/API allows to manage ApplicationSet resources same as Argo CD Applications ([#7352](https://github.com/argoproj/argo-cd/issues/7352)).
## v2.3
### Merge Argo CD Notifications into Argo CD
The [Argo CD Notifications](https://github.com/argoproj-labs/argocd-notifications) should be merged into Argo CD and available out-of-the-box: [#7350](https://github.com/argoproj/argo-cd/issues/7350)
### Merge Argo CD Image Updater into Argo CD
### Input Forms UI Refresh
The [Argo CD Image Updater](https://github.com/argoproj-labs/argocd-image-updater) should be merged into Argo CD and available out-of-the-box: [#7385](https://github.com/argoproj/argo-cd/issues/7385)
Improved design of the input forms in Argo CD Web UI: https://www.figma.com/file/IIlsFqqmM5UhqMVul9fQNq/Argo-CD?node-id=0%3A1
### Compact resources tree
An ability to collaps leaf resources tree to improve visualization of very large applications: [#7349](https://github.com/argoproj/argo-cd/issues/7349)
### Maintain difference in cluster and git values for specific fields
The feature allows to avoid updating fields excluded from diffing ([#2913](https://github.com/argoproj/argo-cd/issues/2913)).
### Web Shell
Exec into the Kubernetes Pod right from Argo CD Web UI! [#4351](https://github.com/argoproj/argo-cd/issues/4351)
@@ -82,6 +56,21 @@ Exec into the Kubernetes Pod right from Argo CD Web UI! [#4351](https://github.c
The feature allows combining of-the-shelf Helm chart and value file in Git repository ([#2789](https://github.com/argoproj/argo-cd/issues/2789))
## v2.4 and beyond
### Merge ApplicationSet controller into Argo CD
The ApplicationSet functionality is available in Argo CD out-of-the-box ([#7351](https://github.com/argoproj/argo-cd/issues/7351)).
The Argo CD UI/CLI/API allows to manage ApplicationSet resources same as Argo CD Applications ([#7352](https://github.com/argoproj/argo-cd/issues/7352)).
### Merge Argo CD Image Updater into Argo CD
The [Argo CD Image Updater](https://github.com/argoproj-labs/argocd-image-updater) should be merged into Argo CD and available out-of-the-box: [#7385](https://github.com/argoproj/argo-cd/issues/7385)
### Config Management Tools Integrations UI/CLI
The continuation of the Config Management Tools of [proposal](https://github.com/argoproj/argo-cd/pull/5927). The Argo CD UI/CLI
@@ -96,9 +85,6 @@ visualize custom resources that don't have owner references.
The feature allows specifying dependencies between applications that allow orchestrating synchronization of multiple applications. [#3517](https://github.com/argoproj/argo-cd/issues/3517)
### Maintain difference in cluster and git values for specific fields
The feature allows to avoid updating fields excluded from diffing ([#2913](https://github.com/argoproj/argo-cd/issues/2913)).
### Multi-tenancy improvements
@@ -119,6 +105,25 @@ A lot of Argo CD features are still not available in GitOps engine. The followin
## Completed
### ✅ Config Management Tools Integrations ([proposal](https://github.com/argoproj/argo-cd/pull/5927))
The community likes the first class support of Helm, Kustomize and keeps requesting support for more tools.
Argo CD provides a mechanism to integrate with any config management tool. We need to investigate why
it is not enough and implement missing features.
### ✅ Argo CD Extensions ([proposal](https://github.com/argoproj/argo-cd/pull/6240))
Argo CD supports customizing handling of Kubernetes resources via diffing customizations,
health checks, and custom actions. The Argo CD Extensions proposal takes it to next
level and allows to deliver the resource customizations along with custom visualization in Argo CD
via Git repository.
### ✅ Project scoped repository and clusters ([proposal](https://github.com/argoproj/argo-cd/blob/master/docs/proposals/project-repos-and-clusters.md))
The feature streamlines the process of adding repositories and clusters to the project and makes it self-service.
Instead of asking an administrator to change Argo CD settings end users can perform the change independently.
### ✅ Core Argo CD ([proposal](https://github.com/argoproj/argo-cd/pull/6385))
Core Argo CD allows to installation and use of lightweight Argo CD that includes only the backend without exposing the API or UI.

2
go.mod
View File

@@ -8,7 +8,7 @@ require (
github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d
github.com/alicebob/miniredis v2.5.0+incompatible
github.com/alicebob/miniredis/v2 v2.14.2
github.com/argoproj/gitops-engine v0.5.1
github.com/argoproj/gitops-engine v0.5.2
github.com/argoproj/pkg v0.11.1-0.20211203175135-36c59d8fafe0
github.com/bombsimon/logrusr v1.0.0
github.com/bradleyfalzon/ghinstallation/v2 v2.0.2

4
go.sum
View File

@@ -103,8 +103,8 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYU
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/argoproj/gitops-engine v0.5.1 h1:kIPvoYFRvDA+3Tz2aEjwRBVGIgcuZ+HIzuHcRmvxo54=
github.com/argoproj/gitops-engine v0.5.1/go.mod h1:K2RYpGXh11VdFwDksS23SyFTOJaPcsF+MVJ/FHlqEOE=
github.com/argoproj/gitops-engine v0.5.2 h1:UQ2ajVyUPCSgFyqidzlTXddh/Xf6cE3I0s9uu92BoJg=
github.com/argoproj/gitops-engine v0.5.2/go.mod h1:K2RYpGXh11VdFwDksS23SyFTOJaPcsF+MVJ/FHlqEOE=
github.com/argoproj/pkg v0.11.1-0.20211203175135-36c59d8fafe0 h1:Cfp7rO/HpVxnwlRqJe0jHiBbZ77ZgXhB6HWlYD02Xdc=
github.com/argoproj/pkg v0.11.1-0.20211203175135-36c59d8fafe0/go.mod h1:ra+bQPmbVAoEL+gYSKesuigt4m49i3Qa3mE/xQcjCiA=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=

View File

@@ -28,7 +28,7 @@ spec:
name: dexconfig
containers:
- name: dex
image: ghcr.io/dexidp/dex:v2.30.0
image: ghcr.io/dexidp/dex:v2.30.2
imagePullPolicy: Always
command: [/shared/argocd-dex, rundex]
securityContext:

View File

@@ -5,7 +5,7 @@ kind: Kustomization
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v2.2.1
newTag: v2.2.4
resources:
- ./application-controller
- ./dex

View File

@@ -21,7 +21,7 @@ spec:
serviceAccountName: argocd-redis
containers:
- name: redis
image: redis:6.2.4-alpine
image: redis:6.2.6-alpine
imagePullPolicy: Always
args:
- "--save"

View File

@@ -2890,7 +2890,7 @@ spec:
- ""
- --appendonly
- "no"
image: redis:6.2.4-alpine
image: redis:6.2.6-alpine
imagePullPolicy: Always
name: redis
ports:
@@ -3018,7 +3018,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -3067,7 +3067,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
name: copyutil
volumeMounts:
- mountPath: /var/run/argocd
@@ -3232,7 +3232,7 @@ spec:
key: controller.default.cache.expiration
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -11,4 +11,4 @@ resources:
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v2.2.1
newTag: v2.2.4

View File

@@ -11,7 +11,7 @@ patchesStrategicMerge:
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v2.2.1
newTag: v2.2.4
resources:
- ../../base/application-controller
- ../../base/dex

View File

@@ -878,7 +878,7 @@ spec:
automountServiceAccountToken: false
initContainers:
- name: config-init
image: redis:6.2.4-alpine
image: redis:6.2.6-alpine
imagePullPolicy: IfNotPresent
resources:
{}
@@ -906,7 +906,7 @@ spec:
containers:
- name: redis
image: redis:6.2.4-alpine
image: redis:6.2.6-alpine
imagePullPolicy: IfNotPresent
command:
- redis-server
@@ -947,7 +947,7 @@ spec:
lifecycle:
{}
- name: sentinel
image: redis:6.2.4-alpine
image: redis:6.2.6-alpine
imagePullPolicy: IfNotPresent
command:
- redis-sentinel

View File

@@ -15,6 +15,6 @@ redis-ha:
client: 6m
checkInterval: 3s
image:
tag: 6.2.4-alpine
tag: 6.2.6-alpine
sentinel:
bind: "0.0.0.0"

View File

@@ -3687,7 +3687,7 @@ spec:
- command:
- /shared/argocd-dex
- rundex
image: ghcr.io/dexidp/dex:v2.30.0
image: ghcr.io/dexidp/dex:v2.30.2
imagePullPolicy: Always
name: dex
ports:
@@ -3709,7 +3709,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
imagePullPolicy: Always
name: copyutil
volumeMounts:
@@ -3926,7 +3926,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -3975,7 +3975,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
name: copyutil
volumeMounts:
- mountPath: /var/run/argocd
@@ -4202,7 +4202,7 @@ spec:
key: server.http.cookie.maxnumber
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -4398,7 +4398,7 @@ spec:
key: controller.default.cache.expiration
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -4480,7 +4480,7 @@ spec:
- /data/conf/redis.conf
command:
- redis-server
image: redis:6.2.4-alpine
image: redis:6.2.6-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -4518,7 +4518,7 @@ spec:
- /data/conf/sentinel.conf
command:
- redis-sentinel
image: redis:6.2.4-alpine
image: redis:6.2.6-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -4564,7 +4564,7 @@ spec:
value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4
- name: SENTINEL_ID_2
value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca
image: redis:6.2.4-alpine
image: redis:6.2.6-alpine
imagePullPolicy: IfNotPresent
name: config-init
volumeMounts:

View File

@@ -1046,7 +1046,7 @@ spec:
- command:
- /shared/argocd-dex
- rundex
image: ghcr.io/dexidp/dex:v2.30.0
image: ghcr.io/dexidp/dex:v2.30.2
imagePullPolicy: Always
name: dex
ports:
@@ -1068,7 +1068,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
imagePullPolicy: Always
name: copyutil
volumeMounts:
@@ -1285,7 +1285,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -1334,7 +1334,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
name: copyutil
volumeMounts:
- mountPath: /var/run/argocd
@@ -1561,7 +1561,7 @@ spec:
key: server.http.cookie.maxnumber
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -1757,7 +1757,7 @@ spec:
key: controller.default.cache.expiration
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -1839,7 +1839,7 @@ spec:
- /data/conf/redis.conf
command:
- redis-server
image: redis:6.2.4-alpine
image: redis:6.2.6-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -1877,7 +1877,7 @@ spec:
- /data/conf/sentinel.conf
command:
- redis-sentinel
image: redis:6.2.4-alpine
image: redis:6.2.6-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -1923,7 +1923,7 @@ spec:
value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4
- name: SENTINEL_ID_2
value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca
image: redis:6.2.4-alpine
image: redis:6.2.6-alpine
imagePullPolicy: IfNotPresent
name: config-init
volumeMounts:

View File

@@ -3057,7 +3057,7 @@ spec:
- command:
- /shared/argocd-dex
- rundex
image: ghcr.io/dexidp/dex:v2.30.0
image: ghcr.io/dexidp/dex:v2.30.2
imagePullPolicy: Always
name: dex
ports:
@@ -3079,7 +3079,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
imagePullPolicy: Always
name: copyutil
volumeMounts:
@@ -3132,7 +3132,7 @@ spec:
- ""
- --appendonly
- "no"
image: redis:6.2.4-alpine
image: redis:6.2.6-alpine
imagePullPolicy: Always
name: redis
ports:
@@ -3260,7 +3260,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -3309,7 +3309,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
name: copyutil
volumeMounts:
- mountPath: /var/run/argocd
@@ -3532,7 +3532,7 @@ spec:
key: server.http.cookie.maxnumber
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -3722,7 +3722,7 @@ spec:
key: controller.default.cache.expiration
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -416,7 +416,7 @@ spec:
- command:
- /shared/argocd-dex
- rundex
image: ghcr.io/dexidp/dex:v2.30.0
image: ghcr.io/dexidp/dex:v2.30.2
imagePullPolicy: Always
name: dex
ports:
@@ -438,7 +438,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
imagePullPolicy: Always
name: copyutil
volumeMounts:
@@ -491,7 +491,7 @@ spec:
- ""
- --appendonly
- "no"
image: redis:6.2.4-alpine
image: redis:6.2.6-alpine
imagePullPolicy: Always
name: redis
ports:
@@ -619,7 +619,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -668,7 +668,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
name: copyutil
volumeMounts:
- mountPath: /var/run/argocd
@@ -891,7 +891,7 @@ spec:
key: server.http.cookie.maxnumber
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -1081,7 +1081,7 @@ spec:
key: controller.default.cache.expiration
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.2.1
image: quay.io/argoproj/argocd:v2.2.4
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -1569,8 +1569,8 @@ func validatePolicy(proj string, role string, policy string) error {
}
// resource
resource := strings.Trim(policyComponents[2], " ")
if resource != "applications" {
return status.Errorf(codes.InvalidArgument, "invalid policy rule '%s': project resource must be: 'applications', not '%s'", policy, resource)
if resource != "applications" && resource != "repositories" && resource != "clusters" {
return status.Errorf(codes.InvalidArgument, "invalid policy rule '%s': project resource must be: 'applications', 'repositories' or 'clusters', not '%s'", policy, resource)
}
// action
action := strings.Trim(policyComponents[3], " ")
@@ -1579,7 +1579,7 @@ func validatePolicy(proj string, role string, policy string) error {
}
// object
object := strings.Trim(policyComponents[4], " ")
objectRegexp, err := regexp.Compile(fmt.Sprintf(`^%s/[*\w-.]+$`, proj))
objectRegexp, err := regexp.Compile(fmt.Sprintf(`^%s/[*\w-.]+$`, regexp.QuoteMeta(proj)))
if err != nil || !objectRegexp.MatchString(object) {
return status.Errorf(codes.InvalidArgument, "invalid policy rule '%s': object must be of form '%s/*' or '%s/<APPNAME>', not '%s'", policy, proj, proj, object)
}

View File

@@ -2561,3 +2561,22 @@ func TestOrphanedResourcesMonitorSettings_IsWarn(t *testing.T) {
settings.Warn = pointer.BoolPtr(true)
assert.True(t, settings.IsWarn())
}
func Test_validatePolicy_projIsNotRegex(t *testing.T) {
// Make sure the "." in "some.project" isn't treated as the regex wildcard.
err := validatePolicy("some.project", "org-admin", "p, proj:some.project:org-admin, applications, *, some-project/*, allow")
assert.Error(t, err)
err = validatePolicy("some.project", "org-admin", "p, proj:some.project:org-admin, applications, *, some.project/*, allow")
assert.NoError(t, err)
err = validatePolicy("some-project", "org-admin", "p, proj:some-project:org-admin, applications, *, some-project/*, allow")
assert.NoError(t, err)
}
func Test_validatePolicy_ValidResource(t *testing.T) {
err := validatePolicy("some-project", "org-admin", "p, proj:some-project:org-admin, repositories, *, some-project/*, allow")
assert.NoError(t, err)
err = validatePolicy("some-project", "org-admin", "p, proj:some-project:org-admin, clusters, *, some-project/*, allow")
assert.NoError(t, err)
}

View File

@@ -51,7 +51,6 @@ import (
"github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/ksonnet"
"github.com/argoproj/argo-cd/v2/util/kustomize"
"github.com/argoproj/argo-cd/v2/util/security"
"github.com/argoproj/argo-cd/v2/util/text"
)
@@ -65,6 +64,9 @@ const (
ociPrefix = "oci://"
)
// List of protocol schemes allowed for fetching remote value files
var allowedHelmRemoteProtocols = []string{"http", "https"}
// Service implements ManifestService interface
type Service struct {
repoLock *repositoryLock
@@ -553,6 +555,146 @@ func runHelmBuild(appPath string, h helm.Helm) error {
return ioutil.WriteFile(markerFile, []byte("marker"), 0644)
}
// resolveSymbolicLinkRecursive resolves the symlink path recursively to its
// canonical path on the file system, with a maximum nesting level of maxDepth.
// If path is not a symlink, returns the verbatim copy of path and err of nil.
func resolveSymbolicLinkRecursive(path string, maxDepth int) (string, error) {
resolved, err := os.Readlink(path)
if err != nil {
// path is not a symbolic link
_, ok := err.(*os.PathError)
if ok {
return path, nil
}
// Other error has occured
return "", err
}
if maxDepth == 0 {
return "", fmt.Errorf("maximum nesting level reached")
}
return resolveSymbolicLinkRecursive(resolved, maxDepth-1)
}
// isURLSchemeAllowed returns true if the protocol scheme is in the list of
// allowed URL schemes.
func isURLSchemeAllowed(scheme string, allowed []string) bool {
isAllowed := false
if len(allowed) > 0 {
for _, s := range allowed {
if strings.EqualFold(scheme, s) {
isAllowed = true
break
}
}
}
// Empty scheme means local file
return isAllowed && scheme != ""
}
// resolveHelmValueFilePath will inspect and resolve a path to a Helm value
// file, and make sure that its final path is within the boundaries of the
// path specified in repoRoot.
//
// appPath is the path we're operating in, e.g. where a Helm chart was unpacked
// to. repoRoot is the path to the root of the repository.
//
// If either appPath or repoRoot is relative, it will be treated as relative
// to the current working directory.
//
// valueFile is the path to a value file, relative to appPath. If valueFile is
// specified as an absolute path (i.e. leading slash), it will be treated as
// relative to the repoRoot. In case valueFile is a symlink in the extracted
// chart, it will be resolved recursively and the decision of whether it is in
// the boundary of repoRoot will be made using the final resolved path.
// valueFile can also be a remote URL with a protocol scheme as prefix,
// in which case the scheme must be included in the list of allowed schemes
// specified by allowedURLSchemes.
//
// Will return an error if either valueFile is outside the boundaries of the
// repoRoot, valueFile is an URL with a forbidden protocol scheme or if
// valueFile is a recursive symlink nested too deep. May return errors for
// other reasons as well.
//
// resolvedPath will hold the absolute, resolved path for valueFile on success
// or set to the empty string on failure.
//
// isRemote will be set to true if valueFile is an URL using an allowed
// protocol scheme, or to false if it resolved to a local file.
func resolveHelmValueFilePath(appPath, repoRoot, valueFile string, allowedURLSchemes []string) (resolvedPath string, isRemote bool, err error) {
// We do not provide the path in the error message, because it will be
// returned to the user and could be used for information gathering.
// Instead, we log the concrete error details.
resolveFailure := func(path string, err error) error {
log.Errorf("failed to resolve path '%s': %v", path, err)
return fmt.Errorf("internal error: failed to resolve path. Check logs for more details")
}
// A value file can be specified as an URL to a remote resource.
// We only allow certain URL schemes for remote value files.
url, err := url.Parse(valueFile)
if err == nil {
// If scheme is empty, it means we parsed a path only
if url.Scheme != "" {
if isURLSchemeAllowed(url.Scheme, allowedURLSchemes) {
return valueFile, true, nil
} else {
return "", false, fmt.Errorf("the URL scheme '%s' is not allowed", url.Scheme)
}
}
}
// Ensure that our repository root is absolute
absRepoPath, err := filepath.Abs(repoRoot)
if err != nil {
return "", false, resolveFailure(repoRoot, err)
}
// If the path to the file is relative, join it with the current working directory (appPath)
// Otherwise, join it with the repository's root
path := valueFile
if !filepath.IsAbs(path) {
absWorkDir, err := filepath.Abs(appPath)
if err != nil {
return "", false, resolveFailure(repoRoot, err)
}
path = filepath.Join(absWorkDir, path)
} else {
path = filepath.Join(absRepoPath, path)
}
// Ensure any symbolic link is resolved before we
delinkedPath, err := resolveSymbolicLinkRecursive(path, 10)
if err != nil {
return "", false, resolveFailure(path, err)
}
path = delinkedPath
// Resolve the joined path to an absolute path
path, err = filepath.Abs(path)
if err != nil {
return "", false, resolveFailure(path, err)
}
// Ensure our root path has a trailing slash, otherwise the following check
// would return true if root is /foo and path would be /foo2
requiredRootPath := absRepoPath
if !strings.HasSuffix(requiredRootPath, "/") {
requiredRootPath += "/"
}
// Make sure that the resolved path to values file is within the repository's root path
if !strings.HasPrefix(path, requiredRootPath) {
return "", false, fmt.Errorf("value file '%s' resolved to outside repository root", valueFile)
}
return path, false, nil
}
func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclient.ManifestRequest, isLocal bool) ([]*unstructured.Unstructured, error) {
concurrencyAllowed := isConcurrencyAllowed(appPath)
if !concurrencyAllowed {
@@ -582,31 +724,14 @@ func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclie
}
for _, val := range appHelm.ValueFiles {
// If val is not a URL, run it against the directory enforcer. If it is a URL, use it without checking
if _, err := url.ParseRequestURI(val); err != nil {
// Ensure that the repo root provided is absolute
absRepoPath, err := filepath.Abs(repoRoot)
if err != nil {
return nil, err
}
// If the path to the file is relative, join it with the current working directory (appPath)
path := val
if !filepath.IsAbs(path) {
absWorkDir, err := filepath.Abs(appPath)
if err != nil {
return nil, err
}
path = filepath.Join(absWorkDir, path)
}
_, err = security.EnforceToCurrentRoot(absRepoPath, path)
if err != nil {
return nil, err
}
// This will resolve val to an absolute path (or an URL)
path, _, err := resolveHelmValueFilePath(appPath, repoRoot, val, allowedHelmRemoteProtocols)
if err != nil {
return nil, err
}
templateOpts.Values = append(templateOpts.Values, val)
templateOpts.Values = append(templateOpts.Values, path)
}
if appHelm.Values != "" {

View File

@@ -728,7 +728,7 @@ func TestHelmManifestFromChartRepoWithValueFileOutsideRepo(t *testing.T) {
}
request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true}
_, err := service.GenerateManifest(context.Background(), request)
assert.Error(t, err, "should be on or under current directory")
assert.Error(t, err)
}
func TestGenerateHelmWithURL(t *testing.T) {
@@ -751,33 +751,88 @@ func TestGenerateHelmWithURL(t *testing.T) {
// The requested value file (`../../../../../minio/values.yaml`) is outside the repo directory
// (`~/go/src/github.com/argoproj/argo-cd`), so it is blocked
func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) {
service := newService("../..")
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
Repo: &argoappv1.Repository{},
AppName: "test",
ApplicationSource: &argoappv1.ApplicationSource{
Path: "./util/helm/testdata/redis",
Helm: &argoappv1.ApplicationSourceHelm{
ValueFiles: []string{"../../../../../minio/values.yaml"},
Values: `cluster: {slaveCount: 2}`,
t.Run("Values file with relative path pointing outside repo root", func(t *testing.T) {
service := newService("../..")
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
Repo: &argoappv1.Repository{},
AppName: "test",
ApplicationSource: &argoappv1.ApplicationSource{
Path: "./util/helm/testdata/redis",
Helm: &argoappv1.ApplicationSourceHelm{
ValueFiles: []string{"../../../../../minio/values.yaml"},
Values: `cluster: {slaveCount: 2}`,
},
},
},
})
assert.Error(t, err)
assert.Contains(t, err.Error(), "outside repository root")
})
assert.Error(t, err, "should be on or under current directory")
service = newService("./testdata/my-chart")
_, err = service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
Repo: &argoappv1.Repository{},
AppName: "test",
ApplicationSource: &argoappv1.ApplicationSource{
Path: ".",
Helm: &argoappv1.ApplicationSourceHelm{
ValueFiles: []string{"../my-chart-2/values.yaml"},
Values: `cluster: {slaveCount: 2}`,
t.Run("Values file with relative path pointing inside repo root", func(t *testing.T) {
service := newService("./testdata/my-chart")
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
Repo: &argoappv1.Repository{},
AppName: "test",
ApplicationSource: &argoappv1.ApplicationSource{
Path: ".",
Helm: &argoappv1.ApplicationSourceHelm{
ValueFiles: []string{"../my-chart/my-chart-values.yaml"},
Values: `cluster: {slaveCount: 2}`,
},
},
},
})
assert.NoError(t, err)
})
t.Run("Values file with absolute path stays within repo root", func(t *testing.T) {
service := newService("./testdata/my-chart")
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
Repo: &argoappv1.Repository{},
AppName: "test",
ApplicationSource: &argoappv1.ApplicationSource{
Path: ".",
Helm: &argoappv1.ApplicationSourceHelm{
ValueFiles: []string{"/my-chart-values.yaml"},
Values: `cluster: {slaveCount: 2}`,
},
},
})
assert.NoError(t, err)
})
t.Run("Values file with absolute path using back-references outside repo root", func(t *testing.T) {
service := newService("./testdata/my-chart")
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
Repo: &argoappv1.Repository{},
AppName: "test",
ApplicationSource: &argoappv1.ApplicationSource{
Path: ".",
Helm: &argoappv1.ApplicationSourceHelm{
ValueFiles: []string{"/../../../my-chart-values.yaml"},
Values: `cluster: {slaveCount: 2}`,
},
},
})
assert.Error(t, err)
assert.Contains(t, err.Error(), "outside repository root")
})
t.Run("Remote values file from forbidden protocol", func(t *testing.T) {
service := newService("./testdata/my-chart")
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
Repo: &argoappv1.Repository{},
AppName: "test",
ApplicationSource: &argoappv1.ApplicationSource{
Path: ".",
Helm: &argoappv1.ApplicationSourceHelm{
ValueFiles: []string{"file://../../../../my-chart-values.yaml"},
Values: `cluster: {slaveCount: 2}`,
},
},
})
assert.Error(t, err)
assert.Contains(t, err.Error(), "is not allowed")
})
assert.Error(t, err, "should be on or under current directory")
}
// The requested file parameter (`/tmp/external-secret.txt`) is outside the app path
@@ -1561,3 +1616,178 @@ func Test_getHelmDependencyRepos(t *testing.T) {
assert.Equal(t, repos[0].Repo, repo1)
assert.Equal(t, repos[1].Repo, repo2)
}
func Test_resolveSymlinkRecursive(t *testing.T) {
cwd, err := os.Getwd()
require.NoError(t, err)
err = os.Chdir("testdata/symlinks")
require.NoError(t, err)
defer func() {
err := os.Chdir(cwd)
if err != nil {
panic(err)
}
}()
t.Run("Resolve non-symlink", func(t *testing.T) {
r, err := resolveSymbolicLinkRecursive("foo", 2)
assert.NoError(t, err)
assert.Equal(t, "foo", r)
})
t.Run("Successfully resolve symlink", func(t *testing.T) {
r, err := resolveSymbolicLinkRecursive("bar", 2)
assert.NoError(t, err)
assert.Equal(t, "foo", r)
})
t.Run("Do not allow symlink at all", func(t *testing.T) {
r, err := resolveSymbolicLinkRecursive("bar", 0)
assert.Error(t, err)
assert.Equal(t, "", r)
})
t.Run("Error because too nested symlink", func(t *testing.T) {
r, err := resolveSymbolicLinkRecursive("bam", 2)
assert.Error(t, err)
assert.Equal(t, "", r)
})
t.Run("No such file or directory", func(t *testing.T) {
r, err := resolveSymbolicLinkRecursive("foobar", 2)
assert.NoError(t, err)
assert.Equal(t, "foobar", r)
})
}
func Test_isURLSchemeAllowed(t *testing.T) {
type testdata struct {
name string
scheme string
allowed []string
expected bool
}
var tts []testdata = []testdata{
{
name: "Allowed scheme matches",
scheme: "http",
allowed: []string{"http", "https"},
expected: true,
},
{
name: "Allowed scheme matches only partially",
scheme: "http",
allowed: []string{"https"},
expected: false,
},
{
name: "Scheme is not allowed",
scheme: "file",
allowed: []string{"http", "https"},
expected: false,
},
{
name: "Empty scheme with valid allowances is forbidden",
scheme: "",
allowed: []string{"http", "https"},
expected: false,
},
{
name: "Empty scheme with empty allowances is forbidden",
scheme: "",
allowed: []string{},
expected: false,
},
{
name: "Some scheme with empty allowances is forbidden",
scheme: "file",
allowed: []string{},
expected: false,
},
}
for _, tt := range tts {
t.Run(tt.name, func(t *testing.T) {
r := isURLSchemeAllowed(tt.scheme, tt.allowed)
assert.Equal(t, tt.expected, r)
})
}
}
func Test_resolveHelmValueFilePath(t *testing.T) {
t.Run("Resolve normal relative path into absolute path", func(t *testing.T) {
p, remote, err := resolveHelmValueFilePath("/foo/bar", "/foo", "baz/bim.yaml", allowedHelmRemoteProtocols)
assert.NoError(t, err)
assert.False(t, remote)
assert.Equal(t, "/foo/bar/baz/bim.yaml", p)
})
t.Run("Resolve normal relative path into absolute path", func(t *testing.T) {
p, remote, err := resolveHelmValueFilePath("/foo/bar", "/foo", "baz/../../bim.yaml", allowedHelmRemoteProtocols)
assert.NoError(t, err)
assert.False(t, remote)
assert.Equal(t, "/foo/bim.yaml", p)
})
t.Run("Error on path resolving outside repository root", func(t *testing.T) {
p, remote, err := resolveHelmValueFilePath("/foo/bar", "/foo", "baz/../../../bim.yaml", allowedHelmRemoteProtocols)
assert.Error(t, err)
assert.Contains(t, err.Error(), "outside repository root")
assert.False(t, remote)
assert.Equal(t, "", p)
})
t.Run("Return verbatim URL", func(t *testing.T) {
url := "https://some.where/foo,yaml"
p, remote, err := resolveHelmValueFilePath("/foo/bar", "/foo", url, allowedHelmRemoteProtocols)
assert.NoError(t, err)
assert.True(t, remote)
assert.Equal(t, url, p)
})
t.Run("URL scheme not allowed", func(t *testing.T) {
url := "file:///some.where/foo,yaml"
p, remote, err := resolveHelmValueFilePath("/foo/bar", "/foo", url, allowedHelmRemoteProtocols)
assert.Error(t, err)
assert.False(t, remote)
assert.Equal(t, "", p)
})
t.Run("Implicit URL by absolute path", func(t *testing.T) {
p, remote, err := resolveHelmValueFilePath("/foo/bar", "/foo", "/baz.yaml", allowedHelmRemoteProtocols)
assert.NoError(t, err)
assert.False(t, remote)
assert.Equal(t, "/foo/baz.yaml", p)
})
t.Run("Relative app path", func(t *testing.T) {
p, remote, err := resolveHelmValueFilePath(".", "/foo", "/baz.yaml", allowedHelmRemoteProtocols)
assert.NoError(t, err)
assert.False(t, remote)
assert.Equal(t, "/foo/baz.yaml", p)
})
t.Run("Relative repo path", func(t *testing.T) {
c, err := os.Getwd()
require.NoError(t, err)
p, remote, err := resolveHelmValueFilePath(".", ".", "baz.yaml", allowedHelmRemoteProtocols)
assert.NoError(t, err)
assert.False(t, remote)
assert.Equal(t, c+"/baz.yaml", p)
})
t.Run("Overlapping root prefix without trailing slash", func(t *testing.T) {
p, remote, err := resolveHelmValueFilePath(".", "/foo", "../foo2/baz.yaml", allowedHelmRemoteProtocols)
assert.Error(t, err)
assert.Contains(t, err.Error(), "outside repository root")
assert.False(t, remote)
assert.Equal(t, "", p)
})
t.Run("Overlapping root prefix with trailing slash", func(t *testing.T) {
p, remote, err := resolveHelmValueFilePath(".", "/foo/", "../foo2/baz.yaml", allowedHelmRemoteProtocols)
assert.Error(t, err)
assert.Contains(t, err.Error(), "outside repository root")
assert.False(t, remote)
assert.Equal(t, "", p)
})
t.Run("Garbage input as values file", func(t *testing.T) {
p, remote, err := resolveHelmValueFilePath(".", "/foo/", "kfdj\\ks&&&321209.,---e32908923%$§!\"", allowedHelmRemoteProtocols)
assert.Error(t, err)
assert.Contains(t, err.Error(), "outside repository root")
assert.False(t, remote)
assert.Equal(t, "", p)
})
t.Run("NUL-byte path input as values file", func(t *testing.T) {
p, remote, err := resolveHelmValueFilePath(".", "/foo/", "\000", allowedHelmRemoteProtocols)
assert.Error(t, err)
assert.Contains(t, err.Error(), "outside repository root")
assert.False(t, remote)
assert.Equal(t, "", p)
})
}

View File

@@ -0,0 +1 @@
baz

View File

@@ -0,0 +1 @@
foo

View File

@@ -0,0 +1 @@
bar

View File

@@ -0,0 +1 @@
hello

View File

@@ -2,10 +2,10 @@ health_status = {}
if obj.status ~= nil then
if obj.status.ingress ~= nil then
numIngressRules = 0
numTrue = 0
numFalse = 0
for _, ingressRules in pairs(obj.status.ingress) do
numIngressRules = numIngressRules + 1
numTrue = 0
numFalse = 0
if obj.status.ingress ~= nil then
for _, condition in pairs(ingressRules.conditions) do
if condition.type == "Admitted" and condition.status == "True" then

View File

@@ -213,7 +213,7 @@ func (s *Server) Create(ctx context.Context, q *application.ApplicationCreateReq
return existing, nil
}
if q.Upsert == nil || !*q.Upsert {
return nil, status.Errorf(codes.InvalidArgument, argo.GenerateSpecIsDifferentErrorMessage("application", existing.Spec, a.Spec))
return nil, status.Errorf(codes.InvalidArgument, "existing application spec is different, use upsert flag to force update")
}
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, appRBACName(a)); err != nil {
return nil, err

View File

@@ -3,20 +3,20 @@ package cluster
import (
"time"
"github.com/argoproj/argo-cd/v2/util/argo"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
log "github.com/sirupsen/logrus"
"golang.org/x/net/context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/kubernetes"
"github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster"
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
servercache "github.com/argoproj/argo-cd/v2/server/cache"
"github.com/argoproj/argo-cd/v2/server/rbacpolicy"
"github.com/argoproj/argo-cd/v2/util/argo"
"github.com/argoproj/argo-cd/v2/util/clusterauth"
"github.com/argoproj/argo-cd/v2/util/db"
"github.com/argoproj/argo-cd/v2/util/rbac"
@@ -181,6 +181,15 @@ var clusterFieldsByPath = map[string]func(updated *appv1.Cluster, existing *appv
"clusterResources": func(updated *appv1.Cluster, existing *appv1.Cluster) {
updated.ClusterResources = existing.ClusterResources
},
"labels": func(updated *appv1.Cluster, existing *appv1.Cluster) {
updated.Labels = existing.Labels
},
"annotations": func(updated *appv1.Cluster, existing *appv1.Cluster) {
updated.Annotations = existing.Annotations
},
"project": func(updated *appv1.Cluster, existing *appv1.Cluster) {
updated.Project = existing.Project
},
}
// Update updates a cluster
@@ -197,9 +206,12 @@ func (s *Server) Update(ctx context.Context, q *cluster.ClusterUpdateRequest) (*
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, createRBACObject(c.Project, q.Cluster.Server)); err != nil {
return nil, err
}
// verify that user can do update inside project where cluster will be located
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, createRBACObject(q.Cluster.Project, q.Cluster.Server)); err != nil {
return nil, err
if len(q.UpdatedFields) == 0 || sets.NewString(q.UpdatedFields...).Has("project") {
// verify that user can do update inside project where cluster will be located
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, createRBACObject(q.Cluster.Project, q.Cluster.Server)); err != nil {
return nil, err
}
}
if len(q.UpdatedFields) != 0 {

View File

@@ -109,4 +109,52 @@ func TestUpdateCluster_FieldsPathSet(t *testing.T) {
assert.Equal(t, updated.Name, "minikube")
assert.Equal(t, updated.Namespaces, []string{"default", "kube-system"})
assert.Equal(t, *updated.Shard, int64(1))
labelEnv := map[string]string{
"env": "qa",
}
_, err = server.Update(context.Background(), &clusterapi.ClusterUpdateRequest{
Cluster: &v1alpha1.Cluster{
Server: "https://127.0.0.1",
Labels: labelEnv,
},
UpdatedFields: []string{"labels"},
})
require.NoError(t, err)
assert.Equal(t, updated.Name, "minikube")
assert.Equal(t, updated.Namespaces, []string{"default", "kube-system"})
assert.Equal(t, updated.Labels, labelEnv)
annotationEnv := map[string]string{
"env": "qa",
}
_, err = server.Update(context.Background(), &clusterapi.ClusterUpdateRequest{
Cluster: &v1alpha1.Cluster{
Server: "https://127.0.0.1",
Annotations: annotationEnv,
},
UpdatedFields: []string{"annotations"},
})
require.NoError(t, err)
assert.Equal(t, updated.Name, "minikube")
assert.Equal(t, updated.Namespaces, []string{"default", "kube-system"})
assert.Equal(t, updated.Annotations, annotationEnv)
_, err = server.Update(context.Background(), &clusterapi.ClusterUpdateRequest{
Cluster: &v1alpha1.Cluster{
Server: "https://127.0.0.1",
Project: "new-project",
},
UpdatedFields: []string{"project"},
})
require.NoError(t, err)
assert.Equal(t, updated.Name, "minikube")
assert.Equal(t, updated.Namespaces, []string{"default", "kube-system"})
assert.Equal(t, updated.Project, "new-project")
}

View File

@@ -167,8 +167,8 @@ func (p *RBACPolicyEnforcer) getProjectFromRequest(rvals ...interface{}) *v1alph
if res, ok := rvals[1].(string); ok {
if obj, ok := rvals[3].(string); ok {
switch res {
case ResourceApplications:
if objSplit := strings.Split(obj, "/"); len(objSplit) == 2 {
case ResourceApplications, ResourceRepositories, ResourceClusters:
if objSplit := strings.Split(obj, "/"); len(objSplit) >= 2 {
return getProjectByName(objSplit[0])
}
case ResourceProjects:

View File

@@ -149,3 +149,13 @@ func TestGetScopes_CustomScopes(t *testing.T) {
scopes := rbacEnforcer.GetScopes()
assert.Equal(t, scopes, customScopes)
}
func Test_getProjectFromRequest(t *testing.T) {
fp := newFakeProj()
projLister := test.NewFakeProjLister(fp)
rbacEnforcer := NewRBACPolicyEnforcer(nil, projLister)
project := rbacEnforcer.getProjectFromRequest("", "repositories", "create", fp.Name+"/https://github.com/argoproj/argocd-example-apps")
assert.Equal(t, project.Name, fp.Name)
}

View File

@@ -1,4 +1,4 @@
FROM redis:6.2.4 as redis
FROM redis:6.2.6-alpine as redis
FROM node:12.18.4 as node

View File

@@ -27,11 +27,11 @@ const retryOptions: Array<(formApi: FormApi) => React.ReactNode> = [
];
const defaultInitialValues = {
limit: '',
limit: 2,
backoff: {
duration: '',
maxDuration: '',
factor: ''
duration: '5s',
maxDuration: '3m0s',
factor: 2
}
};

View File

@@ -17,10 +17,10 @@ const retryOptionsView: Array<(initData: models.RetryStrategy) => React.ReactNod
initData => buildRetryOptionView('Limit', initData?.limit),
initData => buildRetryOptionView('Duration', initData?.backoff?.duration),
initData => buildRetryOptionView('Max Duration', initData?.backoff?.maxDuration),
initData => buildRetryOptionView('Factor', initData?.backoff.factor)
initData => buildRetryOptionView('Factor', initData?.backoff?.factor)
];
export const ApplicationRetryView = ({initValues}: {initValues?: models.RetryStrategy}) => {
const result = !initValues ? 'Retry not installed' : retryOptionsView.map((render, i) => render(initValues));
const result = !initValues ? 'Retry disabled' : retryOptionsView.map((render, i) => render(initValues));
return <div className='application-retry-option-view-list'>{result}</div>;
};

View File

@@ -176,8 +176,10 @@ export const ApplicationSyncPanel = ({application, selectedResource, hide}: {app
let contentEnd = resKey.substr(-Math.floor(resKey.length / 2));
// We want the ellipsis to be in the middle of our text, so we use RTL layout to put it there.
// Unfortunately, strong LTR characters get jumbled around, so make sure that the last character isn't strong.
const indexOfFirstLetter = /[a-z]/i.exec(contentEnd).index;
contentEnd = contentEnd.slice(indexOfFirstLetter);
const firstLetter = /[a-z]/i.exec(contentEnd);
if (firstLetter) {
contentEnd = contentEnd.slice(firstLetter.index);
}
const isLongLabel = resKey.length > 68;
return (
<div key={resKey} className='application-sync-panel__resource'>

View File

@@ -607,16 +607,10 @@ func GetPermittedRepos(proj *argoappv1.AppProject, repos []*argoappv1.Repository
}
func getDestinationServer(ctx context.Context, db db.ArgoDB, clusterName string) (string, error) {
clusterList, err := db.ListClusters(ctx)
servers, err := db.GetClusterServersByName(ctx, clusterName)
if err != nil {
return "", err
}
var servers []string
for _, c := range clusterList.Items {
if c.Name == clusterName {
servers = append(servers, c.Server)
}
}
if len(servers) > 1 {
return "", fmt.Errorf("there are %d clusters with the same name: %v", len(servers), servers)
} else if len(servers) == 0 {

View File

@@ -680,14 +680,7 @@ func TestValidateDestination(t *testing.T) {
}
db := &dbmocks.ArgoDB{}
db.On("ListClusters", context.Background()).Return(&argoappv1.ClusterList{
Items: []argoappv1.Cluster{
{
Name: "minikube",
Server: "https://127.0.0.1:6443",
},
},
}, nil)
db.On("GetClusterServersByName", context.Background(), "minikube").Return([]string{"https://127.0.0.1:6443"}, nil)
appCond := ValidateDestination(context.Background(), &dest, db)
assert.Nil(t, appCond)
@@ -707,13 +700,13 @@ func TestValidateDestination(t *testing.T) {
assert.False(t, dest.IsServerInferred())
})
t.Run("List clusters fails", func(t *testing.T) {
t.Run("GetClusterServersByName fails", func(t *testing.T) {
dest := argoappv1.ApplicationDestination{
Name: "minikube",
}
db := &dbmocks.ArgoDB{}
db.On("ListClusters", context.Background()).Return(nil, fmt.Errorf("an error occurred"))
db.On("GetClusterServersByName", context.Background(), mock.Anything).Return(nil, fmt.Errorf("an error occurred"))
err := ValidateDestination(context.Background(), &dest, db)
assert.Equal(t, "unable to find destination server: an error occurred", err.Error())
@@ -726,14 +719,7 @@ func TestValidateDestination(t *testing.T) {
}
db := &dbmocks.ArgoDB{}
db.On("ListClusters", context.Background()).Return(&argoappv1.ClusterList{
Items: []argoappv1.Cluster{
{
Name: "dind",
Server: "https://127.0.0.1:6443",
},
},
}, nil)
db.On("GetClusterServersByName", context.Background(), "minikube").Return(nil, nil)
err := ValidateDestination(context.Background(), &dest, db)
assert.Equal(t, "unable to find destination server: there are no clusters with this name: minikube", err.Error())
@@ -746,18 +732,7 @@ func TestValidateDestination(t *testing.T) {
}
db := &dbmocks.ArgoDB{}
db.On("ListClusters", context.Background()).Return(&argoappv1.ClusterList{
Items: []argoappv1.Cluster{
{
Name: "dind",
Server: "https://127.0.0.1:2443",
},
{
Name: "dind",
Server: "https://127.0.0.1:8443",
},
},
}, nil)
db.On("GetClusterServersByName", context.Background(), "dind").Return([]string{"https://127.0.0.1:2443", "https://127.0.0.1:8443"}, nil)
err := ValidateDestination(context.Background(), &dest, db)
assert.Equal(t, "unable to find destination server: there are 2 clusters with the same name: [https://127.0.0.1:2443 https://127.0.0.1:8443]", err.Error())

View File

@@ -232,6 +232,34 @@ func (db *db) GetProjectClusters(ctx context.Context, project string) ([]*appv1.
return res, nil
}
func (db *db) GetClusterServersByName(ctx context.Context, name string) ([]string, error) {
informer, err := db.settingsMgr.GetSecretsInformer()
if err != nil {
return nil, err
}
// if local cluster name is not overridden and specified name is local cluster name, return local cluster server
localClusterSecrets, err := informer.GetIndexer().ByIndex(settings.ByClusterURLIndexer, appv1.KubernetesInternalAPIServerAddr)
if err != nil {
return nil, err
}
if len(localClusterSecrets) == 0 && db.getLocalCluster().Name == name {
return []string{appv1.KubernetesInternalAPIServerAddr}, nil
}
secrets, err := informer.GetIndexer().ByIndex(settings.ByClusterNameIndexer, name)
if err != nil {
return nil, err
}
var res []string
for i := range secrets {
s := secrets[i].(*apiv1.Secret)
res = append(res, strings.TrimRight(string(s.Data["server"]), "/"))
}
return res, nil
}
// UpdateCluster updates a cluster
func (db *db) UpdateCluster(ctx context.Context, c *appv1.Cluster) (*appv1.Cluster, error) {
clusterSecret, err := db.getClusterSecret(c.Server)

View File

@@ -27,8 +27,10 @@ type ArgoDB interface {
handleAddEvent func(cluster *appv1.Cluster),
handleModEvent func(oldCluster *appv1.Cluster, newCluster *appv1.Cluster),
handleDeleteEvent func(clusterServer string)) error
// GetCluster get returns a cluster by given server url
// GetCluster returns a cluster by given server url
GetCluster(ctx context.Context, server string) (*appv1.Cluster, error)
// GetClusterServersByName returns a cluster server urls by given cluster name
GetClusterServersByName(ctx context.Context, name string) ([]string, error)
// GetProjectClusters return project scoped clusters by given project name
GetProjectClusters(ctx context.Context, project string) ([]*appv1.Cluster, error)
// UpdateCluster updates a cluster

View File

@@ -693,3 +693,59 @@ func TestHelmRepositorySecretsTrim(t *testing.T) {
assert.Equal(t, tt.expectedSecret, tt.retrievedSecret)
}
}
func TestGetClusterServersByName(t *testing.T) {
clientset := getClientset(nil, &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-secret",
Namespace: testNamespace,
Labels: map[string]string{
common.LabelKeySecretType: common.LabelValueSecretTypeCluster,
},
Annotations: map[string]string{
common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD,
},
},
Data: map[string][]byte{
"name": []byte("my-cluster-name"),
"server": []byte("https://my-cluster-server"),
"config": []byte("{}"),
},
})
db := NewDB(testNamespace, settings.NewSettingsManager(context.Background(), clientset, testNamespace), clientset)
servers, err := db.GetClusterServersByName(context.Background(), "my-cluster-name")
assert.NoError(t, err)
assert.ElementsMatch(t, []string{"https://my-cluster-server"}, servers)
}
func TestGetClusterServersByName_InClusterNotConfigured(t *testing.T) {
clientset := getClientset(nil)
db := NewDB(testNamespace, settings.NewSettingsManager(context.Background(), clientset, testNamespace), clientset)
servers, err := db.GetClusterServersByName(context.Background(), "in-cluster")
assert.NoError(t, err)
assert.ElementsMatch(t, []string{v1alpha1.KubernetesInternalAPIServerAddr}, servers)
}
func TestGetClusterServersByName_InClusterConfigured(t *testing.T) {
clientset := getClientset(nil, &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-secret",
Namespace: testNamespace,
Labels: map[string]string{
common.LabelKeySecretType: common.LabelValueSecretTypeCluster,
},
Annotations: map[string]string{
common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD,
},
},
Data: map[string][]byte{
"name": []byte("in-cluster-renamed"),
"server": []byte(v1alpha1.KubernetesInternalAPIServerAddr),
"config": []byte("{}"),
},
})
db := NewDB(testNamespace, settings.NewSettingsManager(context.Background(), clientset, testNamespace), clientset)
servers, err := db.GetClusterServersByName(context.Background(), "in-cluster-renamed")
assert.NoError(t, err)
assert.ElementsMatch(t, []string{v1alpha1.KubernetesInternalAPIServerAddr}, servers)
}

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v0.0.0-dev. DO NOT EDIT.
// Code generated by mockery v1.0.0. DO NOT EDIT.
package mocks
@@ -242,6 +242,29 @@ func (_m *ArgoDB) GetCluster(ctx context.Context, server string) (*v1alpha1.Clus
return r0, r1
}
// GetClusterServersByName provides a mock function with given fields: ctx, name
func (_m *ArgoDB) GetClusterServersByName(ctx context.Context, name string) ([]string, error) {
ret := _m.Called(ctx, name)
var r0 []string
if rf, ok := ret.Get(0).(func(context.Context, string) []string); ok {
r0 = rf(ctx, name)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]string)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, name)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetProjectClusters provides a mock function with given fields: ctx, project
func (_m *ArgoDB) GetProjectClusters(ctx context.Context, project string) ([]*v1alpha1.Cluster, error) {
ret := _m.Called(ctx, project)

View File

@@ -163,6 +163,23 @@ var (
}
return nil, nil
}
ByClusterNameIndexer = "byClusterName"
byClusterNameIndexerFunc = func(obj interface{}) ([]string, error) {
s, ok := obj.(*apiv1.Secret)
if !ok {
return nil, nil
}
if s.Labels == nil || s.Labels[common.LabelKeySecretType] != common.LabelValueSecretTypeCluster {
return nil, nil
}
if s.Data == nil {
return nil, nil
}
if name, ok := s.Data["name"]; ok {
return []string{string(name)}, nil
}
return nil, nil
}
ByProjectClusterIndexer = "byProjectCluster"
ByProjectRepoIndexer = "byProjectRepo"
byProjectIndexerFunc = func(secretType string) func(obj interface{}) ([]string, error) {
@@ -1044,6 +1061,7 @@ func (mgr *SettingsManager) initialize(ctx context.Context) error {
indexers := cache.Indexers{
cache.NamespaceIndex: cache.MetaNamespaceIndexFunc,
ByClusterURLIndexer: byClusterURLIndexerFunc,
ByClusterNameIndexer: byClusterNameIndexerFunc,
ByProjectClusterIndexer: byProjectIndexerFunc(common.LabelValueSecretTypeCluster),
ByProjectRepoIndexer: byProjectIndexerFunc(common.LabelValueSecretTypeRepository),
}