Compare commits

...

107 Commits

Author SHA1 Message Date
Justin Marquis
eef1ddfd8b chore: disable codeql workflow on cherry-pick branches (#12893)
Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>
2023-04-04 16:45:34 -04:00
gcp-cherry-pick-bot[bot]
38e5b676c9 docs: fix broken version selector (#13102) (#13108)
Signed-off-by: Harold Cheng <niuchangcun@gmail.com>
Co-authored-by: cjc7373 <niuchangcun@gmail.com>
2023-04-04 16:22:42 -04:00
Nobuo Takizawa
bea956674d chore: Bump dex from v2.35.3 to v2.36.0 (#12933)
Signed-off-by: nobuyo <longzechangsheng@gmail.com>
2023-03-24 09:57:05 -04:00
Michael Crenshaw
ed0682d2b5 chore: fix lint (#12972)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-03-23 13:29:28 -04:00
argo-bot
598f79236a Bump version to 2.4.28 2023-03-23 14:44:58 +00:00
argo-bot
bdc043cadc Bump version to 2.4.28 2023-03-23 14:44:53 +00:00
Michael Crenshaw
63f9622b00 Merge pull request from GHSA-2q5c-qw9c-fmvq
* fix: prevent app enumeration

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* more tests, fix incorrect param use

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

similar requests

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* fix merge issue

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* fix CLI to understand permission denied is not a fatal error

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* fix test to expect permission denied instead of validation error

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* upgrade notes

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* remove duplicate test

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

---------

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-03-23 09:22:05 -04:00
argo-bot
4375305a20 Bump version to 2.4.27 2023-03-16 22:14:08 +00:00
argo-bot
2cc2f9fe21 Bump version to 2.4.27 2023-03-16 22:14:00 +00:00
Michael Crenshaw
f8e99a2a13 fix codegen
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

fix codegen

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-03-16 16:11:35 -04:00
dependabot[bot]
4d37861374 chore(deps): bump actions/setup-go from 3.5.0 to 4.0.0 (#12888)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3.5.0 to 4.0.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](6edd4406fa...4d34df0c23)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-16 12:53:38 -04:00
Michael Crenshaw
b0782518d6 docs: fix version numbers in upgrade notes (#12896)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-03-16 12:51:31 -04:00
dependabot[bot]
afe352ba20 chore(deps): bump actions/checkout from 3.3.0 to 3.4.0 (#12889)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.3.0 to 3.4.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](ac59398561...24cb908017)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-16 10:49:46 -04:00
jannfis
9a9498958b build: Enable CI checks on PRs to release branches (#12887)
Signed-off-by: jannfis <jann@mistrust.net>
2023-03-16 09:50:50 -04:00
gcp-cherry-pick-bot[bot]
7f666977e6 test: wait longer after repo server restarted to avoid errors on s390x (#12839) (#12884)
Signed-off-by: Sam Ding <samding@ca.ibm.com>
Co-authored-by: Sam Ding <samding@ca.ibm.com>
2023-03-16 09:33:19 -04:00
Michael Crenshaw
26e07509e6 fix: log plugin commands in a better format (#12260)
* fix: log plugin commands in a better format

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* comments

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

---------

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-03-15 09:43:43 -04:00
Michael Crenshaw
c1967d0b21 fix: support 'project' filter field for backwards-compatibility (#12594)
* fix: support 'project' filter field for backwards-compatibility

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* fix codegen

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* add upgrade notes

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* fix upgrade notes

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* tests

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

---------

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-03-15 09:33:44 -04:00
gcp-cherry-pick-bot[bot]
65e4e429c4 docs: cleanup HA operator manual (#10409) (#12865)
Signed-off-by: Prasad Katti <prasadmkatti@gmail.com>
Co-authored-by: Prasad Katti <prasadmkatti@gmail.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-03-15 09:27:07 -04:00
dependabot[bot]
1cc8eaa49a chore(deps): bump actions/cache from 3.2.6 to 3.3.1 (#12845)
Bumps [actions/cache](https://github.com/actions/cache) from 3.2.6 to 3.3.1.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](69d9d449ac...88522ab9f3)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-14 13:57:04 -04:00
argo-bot
94c73e62f9 Bump version to 2.4.26 2023-03-14 14:11:02 +00:00
argo-bot
27ecf4b3ff Bump version to 2.4.26 2023-03-14 14:10:56 +00:00
Michael Crenshaw
7b72e65562 chore: upgrade https lib to avoid CVE-2022-41723
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-03-13 10:02:52 -04:00
gcp-cherry-pick-bot[bot]
178aa6ad0f docs: Fix Jenkins guide link in understand_the_basics.md (#12814) (#12816)
Signed-off-by: Arkadiusz Podkowa <55452766+czuhajster@users.noreply.github.com>
Co-authored-by: Arkadiusz Podkowa <55452766+czuhajster@users.noreply.github.com>
2023-03-10 16:36:16 -05:00
Shaw Ho
71bd164eb6 fix: Fix the applicationset kind typo (#12690)
Signed-off-by: Shaw Ho <tossmilestone@gmail.com>
2023-03-08 09:36:21 -05:00
argo-bot
e7ca215316 Bump version to 2.4.25 2023-03-07 21:39:02 +00:00
argo-bot
aa25c05156 Bump version to 2.4.25 2023-03-07 21:38:57 +00:00
Michael Crenshaw
0b8e4b356c chore: fix whitespace for codegen
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-03-07 16:35:00 -05:00
gcp-cherry-pick-bot[bot]
8a7d291f99 fix: Validate chat button url only when chatUrl is set (#12655) (#12749) (#12759)
* Validate chat button url only when chatUrl is set



* Add Info Support to argocd USERS.md



* Fix linter error



* Fix linter error



---------

Signed-off-by: Rouke Broersma <rouke.broersma@infosupport.com>
Co-authored-by: Rouke Broersma <rouke.broersma@infosupport.com>
2023-03-07 14:38:28 -05:00
Tsubasa Nagasawa
bb0e260971 fix: suppress Kubernetes API deprecation warnings from application controller (#12067)
Completely suppress warning logs only for log levels that are less than Debug.

Signed-off-by: toVersus <toversus2357@gmail.com>
2023-03-06 16:50:33 -05:00
gcp-cherry-pick-bot[bot]
a0a3246421 docs: Update kustomization example (#12555) (#12740)
...to align with documented usage of kustomize.

As it was, this example stops working with Kustomize v5

Signed-off-by: Jonas Bergler <jonas@bergler.name>
Co-authored-by: Jonas Bergler <jonas@bergler.name>
2023-03-06 16:40:53 -05:00
dependabot[bot]
a6c15a5b3f chore(deps): bump actions/cache from 3.2.5 to 3.2.6 (#12567)
Bumps [actions/cache](https://github.com/actions/cache) from 3.2.5 to 3.2.6.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](6998d139dd...69d9d449ac)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-06 16:36:51 -05:00
dependabot[bot]
52f4a1d32a chore(deps): bump sigstore/cosign-installer from 2.8.1 to 3.0.1 (#12689)
Bumps [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) from 2.8.1 to 3.0.1.
- [Release notes](https://github.com/sigstore/cosign-installer/releases)
- [Commits](9becc61764...c3667d9942)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
---
updated-dependencies:
- dependency-name: sigstore/cosign-installer
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-06 16:35:36 -05:00
gcp-cherry-pick-bot[bot]
148830b788 docs: unset finalizer before deleting an app non-cascadingly (#10949) (#12735)
Signed-off-by: Bo Huang <beyondbill@users.noreply.github.com>
Co-authored-by: Bo Huang <beyondbill@users.noreply.github.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-03-06 14:53:22 -05:00
gcp-cherry-pick-bot[bot]
aed9436d1b fix: ensure certificate gets updated on reload (#12076) (#12694)
* fix: ensure certificate gets updated on reload

Fixes #10707. `GetCertificate` ensures that the most current version of
 `a.settings.Certificate` is used. It's still a bit of a mystery to me
 as to why the reloading of the server does not work for this, since it
 should fulfill the same function.



* fix: remove break from cert changes

With 3553ef8, there's no longer any need to break out of the loop. The
webhook reloading logic needs another look (since it likely no longer
works), but can be handled in another PR.



---------

Signed-off-by: Blake Pettersson <blake.pettersson@gmail.com>
Co-authored-by: Blake Pettersson <blake.pettersson@gmail.com>
2023-03-02 09:27:22 -05:00
gcp-cherry-pick-bot[bot]
9d3d7808cb docs: link directly to HA manifests (#11970) (#12684)
This updates the manifest link directly to the High Availability header in the manifest readme. I chose this over linking to the `ha` folder since it explains the options and links to them.

Signed-off-by: Nicholas Morey <nicholas@morey.tech>
Co-authored-by: Nicholas Morey <nicholas@morey.tech>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-03-01 16:59:37 -05:00
Justin Marquis
fe6d9c9fac chore: upgrade redis to 7.0.8 to avoid several CVEs (#12627)
Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>
2023-02-28 09:28:39 -05:00
Justin Marquis
2895869cec chore: upgrade haproxy to 2.0.31 to avoid multiple CVEs (#12629)
Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>
2023-02-28 09:19:50 -05:00
argo-bot
dbd03d07d2 Bump version to 2.4.24 2023-02-27 14:29:44 +00:00
argo-bot
b49f9fbc74 Bump version to 2.4.24 2023-02-27 14:29:39 +00:00
Michael Crenshaw
f8c7c88abf fix: traverse generator tree when getting requeue time (#12407) (#12409)
* add unit test reproducing




* feat: Begin polishing top bar design (#12327)



* chore: add dist to path to use our kustomize version (#12352)

* chore: add dist to path to use our kustomize version



* correct path



* missed a spot



---------




* fix: when resource does not exist node menu and resource details shou… (#12360)

* fix: when resource does not exist node menu and resource details should still render



* Retrigger CI pipeline



---------




* fix: traverse generator tree when getting requeue time



* fix: traverse generator tree when getting requeue time



* remove duplicate code



* Retrigger CI pipeline



* revert gitignore



* update from code review



---------

Signed-off-by: rumstead <rjumstead@gmail.com>
Signed-off-by: rumstead <37445536+rumstead@users.noreply.github.com>
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Signed-off-by: Joshua Helton <jdoghelton@gmail.com>
Co-authored-by: rumstead <37445536+rumstead@users.noreply.github.com>
Co-authored-by: Remington Breeze <remington@breeze.software>
Co-authored-by: jphelton <jdoghelton@gmail.com>
2023-02-24 16:32:31 -05:00
Justin Marquis
f102988793 chore: use registry.k8s.io instead of k8s.gcr.io (#12362)
Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-02-17 15:13:51 -05:00
dependabot[bot]
2c52907304 chore(deps): bump imjasonh/setup-crane from 0.2 to 0.3 (#12504)
Bumps [imjasonh/setup-crane](https://github.com/imjasonh/setup-crane) from 0.2 to 0.3.
- [Release notes](https://github.com/imjasonh/setup-crane/releases)
- [Commits](e82f1b9a80...00c9e93efa)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
---
updated-dependencies:
- dependency-name: imjasonh/setup-crane
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-17 14:40:11 -05:00
Josh Soref
1d6f8b113c docs: FAQ improvements (#12146)
* docs: use more backticks in FAQ

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

* docs: add FAQ entry

The order in patch list … doesn't match $setElementOrder list: …

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>

---------

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-02-17 14:24:33 -05:00
Dmitriy Mann
5bfcff8631 fix: valid username in webhook URL matching regex (#9055) (#12203)
This commit fixes incorrect regular expression used for URL matching.

Expected behavior: valid user info part is matched => webhook is sent.
Actual behavior: some valid user info is not matched, example: `ssh://user-name@example.com/org/repo` => webhook is not sent.

Context:
 - [RFC 3986 3.2.1 - User Information](https://www.rfc-editor.org/rfc/rfc3986#section-3.2.1)
 - [Username validation regex in shadow Linux package](https://github.com/shadow-maint/shadow/blob/master/libmisc/chkname.c#L36)

Signed-off-by: mdsjip <2284562+mdsjip@users.noreply.github.com>
2023-02-17 14:11:30 -05:00
dependabot[bot]
55289991c6 chore(deps): bump docker/setup-buildx-action from 2.4.0 to 2.4.1 (#12308)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2.4.0 to 2.4.1.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](15c905b16b...f03ac48505)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-17 14:01:11 -05:00
atusy
b007073231 docs: fix typo (#12389)
Signed-off-by: atusy <30277794+atusy@users.noreply.github.com>
2023-02-17 13:57:48 -05:00
dependabot[bot]
536d1d0e10 chore(deps): bump actions/cache from 3.2.4 to 3.2.5 (#12433)
Bumps [actions/cache](https://github.com/actions/cache) from 3.2.4 to 3.2.5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](627f0f41f6...6998d139dd)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-17 13:52:50 -05:00
Wojtek Cichoń
dd44083d78 docs: Updated link to Jenkins and added GitHub Actions link (#12465)
Signed-off-by: Wojtek Cichoń <wojtek.cichon@protonmail.com>
2023-02-17 13:36:40 -05:00
Zadkiel Aharonian
db031bbe94 docs: fix typo in health documentation (#12497)
Signed-off-by: Zadkiel Aharonian <hello@zadkiel.fr>
2023-02-17 13:34:38 -05:00
Saumeya Katyal
ac0b3e76c0 fix(security): add url validation for help chat (#9956) (#10417)
* fix: add url validation for help chat

Signed-off-by: saumeya <saumeyakatyal@gmail.com>

* lint check

Signed-off-by: saumeya <saumeyakatyal@gmail.com>

* lint fix

Signed-off-by: saumeya <saumeyakatyal@gmail.com>

* review comments

Signed-off-by: saumeya <saumeyakatyal@gmail.com>

---------

Signed-off-by: saumeya <saumeyakatyal@gmail.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-02-17 13:19:47 -05:00
Michael Chen
a9289d2381 docs: Clarify cascade delete of resource and finalizer (#11064)
* Clarify cascade delete of resource and finalizer.

The wording of this warning was confusing.

Signed-off-by: Michael Chen <4326639+mcgitty@users.noreply.github.com>

* Update docs/operator-manual/declarative-setup.md

Co-authored-by: Nicholas Morey <nicholas@morey.tech>
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

---------

Signed-off-by: Michael Chen <4326639+mcgitty@users.noreply.github.com>
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: Nicholas Morey <nicholas@morey.tech>
2023-02-17 13:18:06 -05:00
schakrad
4be0ce491d fix: show full event message in pod event view (#12104) (#12267)
* #11602 fix : Object options menu truncated when selected in ApplicationListView.

Signed-off-by: schakradari <saisindhu_chakradari@intuit.com>

* fix for the message to be fully shown under the events section.

Signed-off-by: schakradari <saisindhu_chakradari@intuit.com>

* fixing lint

Signed-off-by: schakradari <saisindhu_chakradari@intuit.com>

* Update application-resource-list.tsx

Signed-off-by: schakrad <58915923+schakrad@users.noreply.github.com>

* fix for  lint error

Signed-off-by: schakradari <saisindhu_chakradari@intuit.com>

---------

Signed-off-by: schakradari <saisindhu_chakradari@intuit.com>
Signed-off-by: schakrad <58915923+schakrad@users.noreply.github.com>
2023-02-17 11:58:14 -05:00
argo-bot
e86cf5d705 Bump version to 2.4.23 2023-02-16 14:40:28 +00:00
argo-bot
8f0d27705e Bump version to 2.4.23 2023-02-16 14:40:22 +00:00
Michael Crenshaw
5831a573aa Merge pull request from GHSA-3jfq-742w-xg8j
fix test name

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-02-16 09:07:57 -05:00
Michael Crenshaw
90869357fb chore: add dist to path to use our kustomize version (#12352)
* chore: add dist to path to use our kustomize version

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* correct path

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* missed a spot

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

---------

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-02-09 12:18:33 -05:00
Josh Soref
d0551e4c1a docs: Fix heading to not include a v for the second version (#12218)
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-02-03 10:29:27 -05:00
Thomas Decaux
402b38dc7d docs: add destination.name example (#12242)
I had trouble finding the documentation to use the cluster name for destination, instead of the full URL. This is really useful.

Use case: we manage multiple clusters, destination.name is a better way to set destination.

Signed-off-by: Thomas Decaux <ebuildy@gmail.com>
Signed-off-by: ebuildy <ebuildy@gmail.com>
2023-02-02 12:56:27 -05:00
argo-bot
eadd25e7ed Bump version to 2.4.22 2023-02-02 14:58:52 +00:00
argo-bot
3f98ee249b Bump version to 2.4.22 2023-02-02 14:58:37 +00:00
Panagiotis Georgiadis
cafb09a8a5 fix: Upgrade gopkg.in/yaml.v2 to v2.2.4 (#12247)
Signed-off-by: Panagiotis Georgiadis <pgeorgia@redhat.com>
2023-02-01 16:37:50 -05:00
Jaideep Rao
f7b9fa32e4 fix: Upgrade goutils to v1.1.1 [release-2.4] (#12219) (#12222)
* upgrade goutils to v1.1.1

Signed-off-by: Jaideep Rao <jaideep.r97@gmail.com>

* Update go.mod

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

---------

Signed-off-by: Jaideep Rao <jaideep.r97@gmail.com>
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-02-01 14:25:55 -05:00
dependabot[bot]
8536a84775 chore(deps): bump docker/setup-buildx-action from 2.2.1 to 2.4.0 (#12227)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2.2.1 to 2.4.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](8c0edbc76e...15c905b16b)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-31 16:56:06 -05:00
dependabot[bot]
4bc13fbb25 chore(deps): bump actions/cache from 3.2.3 to 3.2.4 (#12228)
Bumps [actions/cache](https://github.com/actions/cache) from 3.2.3 to 3.2.4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](58c146cc91...627f0f41f6)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-31 16:53:48 -05:00
Adam Jensen
fe6472db05 docs: Fix copy that refers to a different CLI flag (#12236)
Signed-off-by: Adam Jensen <adam@acj.sh>
2023-01-31 16:52:10 -05:00
Kostis (Codefresh)
efb0ceca33 docs: Clarify directory recursion (#12037)
Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>
2023-01-31 16:29:47 -05:00
James Brady
717ac4eec1 docs: Fix list formatting in "Resource Actions" docs page (#12061)
Signed-off-by: James Brady <goodgravy@users.noreply.github.com>
2023-01-31 16:26:46 -05:00
Nobuo Takizawa
6cb2a600b0 chore: Update dex's image tag that is forgotten to be updated (#12234)
Signed-off-by: nobuyo <longzechangsheng@gmail.com>
2023-01-31 15:44:13 -05:00
argo-bot
2eb5656842 Bump version to 2.4.21 2023-01-27 23:14:45 +00:00
argo-bot
5c6f8d5460 Bump version to 2.4.21 2023-01-27 23:14:40 +00:00
Eugen Friedland
45c089b48c fix(health): Handling SparkApplication CRD health status if dynamic allocation is enabled (#7557) (#11522)
Signed-off-by: Yevgeniy Fridland <yevg.mord@gmail.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-27 15:17:27 -05:00
Michael Crenshaw
11ba4834ad fix: add CLI client IDs to default OIDC allowed audiences (#12170) (#12179)
* fix(settings): add CLI client ID in default OAuth2 allowed audiences

Signed-off-by: Yann Soubeyrand <yann.soubeyrand@camptocamp.com>

* fix: add CLI client IDs to default OIDC allowed audiences (#12170)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* docs

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* test

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* handle expired token properly

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

---------

Signed-off-by: Yann Soubeyrand <yann.soubeyrand@camptocamp.com>
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: Yann Soubeyrand <yann.soubeyrand@camptocamp.com>
2023-01-27 14:43:21 -05:00
Leonardo Luz Almeida
8f4678ee1f chore: Refactor terminal handler to use auth-middleware (#12052)
* chore: Refactor terminal handler to use auth-middleware

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>

* remove context key for now

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>

* implement unit-tests

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>

* remove claim valid check for now

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>

* remove unnecessary test

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>

* fix lint

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>

* don't too much details in http response

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>

* Fix error

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>

* Fix lint

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>

* trigger build

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>

* builder pattern in terminal feature-flag middleware

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>
2023-01-27 13:37:02 -05:00
argo-bot
68f58c956a Bump version to 2.4.20 2023-01-25 15:28:44 +00:00
argo-bot
5b3bfc746c Bump version to 2.4.20 2023-01-25 15:28:30 +00:00
Dan Garfield
20c63babca Merge pull request from GHSA-q9hr-j4rf-8fjc
* fix: verify audience claim

Co-Authored-By: Vladimir Pouzanov <farcaller@gmail.com>
Signed-off-by: CI <350466+crenshaw-dev@users.noreply.github.com>

* fix unit tests

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* go mod tidy

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* handle single aud claim marshaled as a string

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

Signed-off-by: CI <350466+crenshaw-dev@users.noreply.github.com>
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: CI <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: Vladimir Pouzanov <farcaller@gmail.com>
2023-01-25 09:15:04 -05:00
Justin Marquis
0775c6e726 chore: disable docker sbom and attestations (#12059)
Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>

Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>
2023-01-20 10:04:20 -05:00
argo-bot
0ba9817cc2 Bump version to 2.4.19 2023-01-18 02:08:53 +00:00
argo-bot
cac46bf3a6 Bump version to 2.4.19 2023-01-18 02:08:46 +00:00
Michael Crenshaw
e89bf08452 chore: upgrade github.com/prometheus/client_golang to v1.11.1 to avoid CVE-2022-21698 (#12015)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-17 17:05:12 -05:00
Michael Crenshaw
4fa0627dd8 chore: fix bad merge
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-17 17:00:56 -05:00
Aymen Ben Tanfous
4665f19f9a fix: Fixed matrix requeueAfterSeconds for PR (#10914) (#10915)
* Fixed matrix requeueAfterSeconds for PR

Signed-off-by: Aymen Ben Tanfous <aymen.bentanfous@gmail.com>

* A try to make some tests

Signed-off-by: Aymen Ben Tanfous <aymen.bentanfous@cimpress.com>

* Fixed default test returns the default time

Signed-off-by: Aymen Ben Tanfous <aymenbentanfous@gmail.com>

* Fixed default test returns the default time

Signed-off-by: Aymen Ben Tanfous <aymenbentanfous@gmail.com>

Signed-off-by: Aymen Ben Tanfous <aymen.bentanfous@gmail.com>
Signed-off-by: Aymen Ben Tanfous <aymen.bentanfous@cimpress.com>
Signed-off-by: Aymen Ben Tanfous <aymenbentanfous@gmail.com>
Co-authored-by: Aymen Ben Tanfous <aymen.bentanfous@cimpress.com>
Co-authored-by: Aymen Ben Tanfous <aymenbentanfous@gmail.com>
2023-01-17 16:54:22 -05:00
dependabot[bot]
32ab1d906f chore(deps): bump actions/cache from 3.2.2 to 3.2.3 (#11928)
Bumps [actions/cache](https://github.com/actions/cache) from 3.2.2 to 3.2.3.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](4723a57e26...58c146cc91)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-13 09:52:10 -05:00
dependabot[bot]
64b3f0f1f4 chore(deps): bump actions/checkout from 3.2.0 to 3.3.0 (#11895)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.2.0 to 3.3.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](755da8c3cf...ac59398561)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-11 14:07:20 -05:00
Michael Crenshaw
70276136b1 chore: fix codegen
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-11 09:47:07 -05:00
Michael Crenshaw
0051144468 chore: fix codegen
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-10 16:58:51 -05:00
dependabot[bot]
df55641842 chore(deps): bump actions/setup-node from 3.5.1 to 3.6.0 (#11896)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3.5.1 to 3.6.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](8c91899e58...64ed1c7eab)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-10 15:25:24 -05:00
dependabot[bot]
7cde38c52f chore(deps): bump actions/upload-artifact from 3.1.1 to 3.1.2 (#11929)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3.1.1 to 3.1.2.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](83fd05a356...0b7f8abb15)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-10 15:22:04 -05:00
Michael Crenshaw
e9a2102593 Revert "fix: ssa e2e tests failing after updating to kubectl 1.26 (#11753)"
This reverts commit a10a54782a.

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-10 12:19:16 -05:00
Michael Crenshaw
56cba50d69 chore: upgrade redis to 7.0.7 to avoid CVE-2022-3996 (#11925)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-10 10:50:44 -05:00
jannfis
556565f167 chore: Upgrade shipped version of Redis to 7.0.5 to fix CVE-2022-35951 (#10702)
* chore: Upgrade redis to 7.0.5

Signed-off-by: jannfis <jann@mistrust.net>

* Also update Redis version in containerized toolchain

Signed-off-by: jannfis <jann@mistrust.net>

* Update Redis and Dex in CI

Signed-off-by: jannfis <jann@mistrust.net>

* Fix Dex image path

Signed-off-by: jannfis <jann@mistrust.net>

Signed-off-by: jannfis <jann@mistrust.net>
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-10 10:44:32 -05:00
Michael Crenshaw
f625165b33 fix: upgrade qs to avoid CVE-2022-24999
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-10 10:22:46 -05:00
Alexander Matyushentsev
d11ee714b8 fix: upgrade superagent to resolve potential CVE (#9494)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2023-01-10 10:21:13 -05:00
dependabot[bot]
3aa191d777 chore(deps): bump actions/cache from 3.2.0 to 3.2.2 (#11839)
Bumps [actions/cache](https://github.com/actions/cache) from 3.2.0 to 3.2.2.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](c17f4bf466...4723a57e26)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-09 17:12:14 -05:00
dependabot[bot]
3487d4789d chore(deps): bump actions/download-artifact from 3.0.1 to 3.0.2 (#11894)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3.0.1 to 3.0.2.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](9782bd6a98...9bc31d5ccc)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-09 16:52:29 -05:00
Michael Crenshaw
9c9fd9685a docs: note risks of secret-injection plugins (#11617)
* docs: note risks of secret-injection plugins

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* grammar tweaks

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* grammar tweaks

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-09 16:31:24 -05:00
dependabot[bot]
bda913c1f2 chore(deps): bump actions/cache from 3.0.11 to 3.2.0 (#11809)
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.11 to 3.2.0.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](9b0c1fce7a...c17f4bf466)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-22 13:39:04 -05:00
Justin Marquis
e37feab905 chore: fix lint error (#11788)
Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>

Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>
2022-12-21 08:14:15 -05:00
Justin Marquis
e380dd1ade chore: get image digest in seperate step (#11778)
* chore: get image digest in seperate step

Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>

* Retrigger CI pipeline

Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>

Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>
2022-12-20 17:22:40 -05:00
Gaël Jourdan-Weil
6c88a166d8 docs: clarify project destination possibilities (#11706)
Clarify that it's possible to reference clusters by `cluster` or by `name`.

Signed-off-by: Gaël Jourdan-Weil <gjourdanweil@gmail.com>

Signed-off-by: Gaël Jourdan-Weil <gjourdanweil@gmail.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2022-12-20 12:52:09 -05:00
Matt Clegg
f007ddffc9 docs: correct SSO configuration URL in example configmap (#11720)
Signed-off-by: Matt Clegg <m@cle.gg>

Signed-off-by: Matt Clegg <m@cle.gg>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2022-12-20 08:58:24 -05:00
Phil Wright- Christie
af7b29d922 docs: Update example dockerfile (#11721)
The latest tag hasn't been updated in almost a year, and as a result, the ubuntu repositories are out of date and are throwing errors. This updates the example to use a fixed version, which are updated much more frequently.

Signed-off-by: Phil Wright- Christie <philwc@gmail.com>

Signed-off-by: Phil Wright- Christie <philwc@gmail.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2022-12-20 08:55:26 -05:00
Leonardo Luz Almeida
a10a54782a fix: ssa e2e tests failing after updating to kubectl 1.26 (#11753)
* fix: ssa e2e test failing after updating to kubectl 1.26

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>

* Remove pinned kubectl version

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>

* Cleaner approach to fix e2e test

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>

* Fix

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>
2022-12-20 08:49:49 -05:00
Nicholas Morey
20e4b8806d docs: clarify that all labels must exist (#11693)
It's unclear if all or any of the labels need to exist. This clarifies that all of the labels must exist.

Signed-off-by: Nicholas Morey <nicholas@morey.tech>

Signed-off-by: Nicholas Morey <nicholas@morey.tech>
2022-12-20 08:47:17 -05:00
dependabot[bot]
2626453cfc chore(deps): bump actions/setup-go from 3.4.0 to 3.5.0 (#11697)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3.4.0 to 3.5.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](d0a58c1c4d...6edd4406fa)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-20 08:41:16 -05:00
yanyx
5d6600ed9d doc: correct kustomize demo path (#11762)
Signed-off-by: Yixing Yan <yixingyan@gmail.com>

Signed-off-by: Yixing Yan <yixingyan@gmail.com>
2022-12-20 08:38:17 -05:00
Justin Marquis
0f3f8db000 fix: sign container images by digest (#11151)
* chore: sign container images by digest

Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>

* use sha hash

Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>

Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>
2022-12-19 20:46:16 -05:00
98 changed files with 3173 additions and 737 deletions

View File

@@ -9,6 +9,7 @@ on:
pull_request:
branches:
- 'master'
- 'release-*'
env:
# Golang version to use across CI steps
@@ -27,9 +28,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
- name: Setup Golang
uses: actions/setup-go@d0a58c1c4d2b25278816e339b944508c875f3613 # v3.4.0
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Download all Go modules
@@ -45,13 +46,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
- name: Setup Golang
uses: actions/setup-go@d0a58c1c4d2b25278816e339b944508c875f3613 # v3.4.0
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Restore go build cache
uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
@@ -69,9 +70,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
- name: Setup Golang
uses: actions/setup-go@d0a58c1c4d2b25278816e339b944508c875f3613 # v3.4.0
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Run golangci-lint
@@ -92,11 +93,11 @@ jobs:
- name: Create checkout directory
run: mkdir -p ~/go/src/github.com/argoproj
- name: Checkout code
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
- name: Create symlink in GOPATH
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
- name: Setup Golang
uses: actions/setup-go@d0a58c1c4d2b25278816e339b944508c875f3613 # v3.4.0
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Install required packages
@@ -116,13 +117,17 @@ jobs:
run: |
echo "/usr/local/bin" >> $GITHUB_PATH
- name: Restore go build cache
uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
- name: Install all tools required for building & testing
run: |
make install-test-tools-local
# We install kustomize in the dist directory
- name: Add dist to PATH
run: |
echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH
- name: Setup git username and email
run: |
git config --global user.name "John Doe"
@@ -133,12 +138,12 @@ jobs:
- name: Run all unit tests
run: make test-local
- name: Generate code coverage artifacts
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # v3.1.1
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with:
name: code-coverage
path: coverage.out
- name: Generate test results artifacts
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # v3.1.1
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with:
name: test-results
path: test-results/
@@ -155,11 +160,11 @@ jobs:
- name: Create checkout directory
run: mkdir -p ~/go/src/github.com/argoproj
- name: Checkout code
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
- name: Create symlink in GOPATH
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
- name: Setup Golang
uses: actions/setup-go@d0a58c1c4d2b25278816e339b944508c875f3613 # v3.4.0
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Install required packages
@@ -179,13 +184,17 @@ jobs:
run: |
echo "/usr/local/bin" >> $GITHUB_PATH
- name: Restore go build cache
uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
- name: Install all tools required for building & testing
run: |
make install-test-tools-local
# We install kustomize in the dist directory
- name: Add dist to PATH
run: |
echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH
- name: Setup git username and email
run: |
git config --global user.name "John Doe"
@@ -196,7 +205,7 @@ jobs:
- name: Run all unit tests
run: make test-race-local
- name: Generate test results artifacts
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # v3.1.1
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with:
name: race-results
path: test-results/
@@ -206,9 +215,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
- name: Setup Golang
uses: actions/setup-go@d0a58c1c4d2b25278816e339b944508c875f3613 # v3.4.0
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Create symlink in GOPATH
@@ -232,6 +241,10 @@ jobs:
make install-codegen-tools-local
make install-go-tools-local
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
# We install kustomize in the dist directory
- name: Add dist to PATH
run: |
echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH
- name: Run codegen
run: |
set -x
@@ -250,14 +263,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
- name: Setup NodeJS
uses: actions/setup-node@8c91899e586c5b171469028077307d293428b516 # v3.5.1
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
node-version: '12.18.4'
- name: Restore node dependency cache
id: cache-dependencies
uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ui/node_modules
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
@@ -287,12 +300,12 @@ jobs:
sonar_secret: ${{ secrets.SONAR_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
with:
fetch-depth: 0
- name: Restore node dependency cache
id: cache-dependencies
uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ui/node_modules
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
@@ -303,11 +316,11 @@ jobs:
run: |
mkdir -p test-results
- name: Get code coverage artifiact
uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7 # v3.0.1
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with:
name: code-coverage
- name: Get test result artifact
uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7 # v3.0.1
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with:
name: test-results
path: test-results
@@ -365,9 +378,9 @@ jobs:
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
- name: Setup Golang
uses: actions/setup-go@d0a58c1c4d2b25278816e339b944508c875f3613 # v3.4.0
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: GH actions workaround - Kill XSP4 process
@@ -393,7 +406,7 @@ jobs:
sudo chown runner $HOME/.kube/config
kubectl version
- name: Restore go build cache
uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
@@ -419,9 +432,9 @@ jobs:
git config --global user.email "john.doe@example.com"
- name: Pull Docker image required for tests
run: |
docker pull ghcr.io/dexidp/dex:v2.35.3
docker pull ghcr.io/dexidp/dex:v2.36.0
docker pull argoproj/argo-cd-ci-builder:v1.0.0
docker pull redis:7.0.4-alpine
docker pull redis:7.0.8-alpine
- name: Create target directory for binaries in the build-process
run: |
mkdir -p dist
@@ -449,7 +462,7 @@ jobs:
set -x
make test-e2e-local
- name: Upload e2e-server logs
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # v3.1.1
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with:
name: e2e-server-k8s${{ matrix.k3s-version }}.log
path: /tmp/e2e-server.log

View File

@@ -5,6 +5,7 @@ on:
# Secrets aren't available for dependabot on push. https://docs.github.com/en/enterprise-cloud@latest/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/troubleshooting-the-codeql-workflow#error-403-resource-not-accessible-by-integration-when-using-dependabot
branches-ignore:
- 'dependabot/**'
- 'cherry-pick-*'
pull_request:
schedule:
- cron: '0 19 * * 0'
@@ -29,7 +30,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL

View File

@@ -28,10 +28,10 @@ jobs:
env:
GOPATH: /home/runner/work/argo-cd/argo-cd
steps:
- uses: actions/setup-go@d0a58c1c4d2b25278816e339b944508c875f3613 # v3.4.0
- uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
- uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
with:
path: src/github.com/argoproj/argo-cd
@@ -53,7 +53,7 @@ jobs:
# build
- uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.1.0
- uses: docker/setup-buildx-action@8c0edbc76e98fa90f69d9a2c020dcb50019dc325 # v2.2.1
- uses: docker/setup-buildx-action@f03ac48505955848960e80bbb68046aa35c7b9e7 # v2.4.1
- name: Setup cache for argocd-ui docker layer
uses: actions/cache@v3
@@ -92,7 +92,7 @@ jobs:
IMAGE_PLATFORMS=linux/amd64,linux/arm64,linux/s390x,linux/ppc64le
fi
echo "Building image for platforms: $IMAGE_PLATFORMS"
docker buildx build --platform $IMAGE_PLATFORMS --push="${{ github.event_name == 'push' }}" \
docker buildx build --platform $IMAGE_PLATFORMS --sbom=false --provenance=false --push="${{ github.event_name == 'push' }}" \
--cache-from "type=local,src=/tmp/.buildx-cache" \
-t ghcr.io/argoproj/argocd:${{ steps.image.outputs.tag }} \
-t quay.io/argoproj/argocd:latest .
@@ -117,13 +117,20 @@ jobs:
# sign container images
- name: Install cosign
uses: sigstore/cosign-installer@9becc617647dfa20ae7b1151972e9b3a2c338a2b # v2.8.1
uses: sigstore/cosign-installer@c3667d99424e7e6047999fb6246c0da843953c65 # v3.0.1
with:
cosign-release: 'v1.13.0'
cosign-release: 'v1.13.1'
- name: Install crane to get digest of image
uses: imjasonh/setup-crane@00c9e93efa4e1138c9a7a5c594acd6c75a2fbf0c
- name: Get digest of image
run: |
echo "IMAGE_DIGEST=$(crane digest quay.io/argoproj/argocd:latest)" >> $GITHUB_ENV
- name: Sign Argo CD latest image
run: |
cosign sign --key env://COSIGN_PRIVATE_KEY quay.io/argoproj/argocd:latest
cosign sign --key env://COSIGN_PRIVATE_KEY quay.io/argoproj/argocd@${{ env.IMAGE_DIGEST }}
# Displays the public key to share.
cosign public-key --key env://COSIGN_PRIVATE_KEY
env:

View File

@@ -12,7 +12,7 @@ on:
- "!release-v0*"
env:
GOLANG_VERSION: '1.18'
GOLANG_VERSION: '1.18'
permissions:
contents: read
@@ -43,7 +43,7 @@ jobs:
GIT_EMAIL: argoproj@gmail.com
steps:
- name: Checkout code
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
@@ -147,7 +147,7 @@ jobs:
echo "RELEASE_NOTES=${RELEASE_NOTES}" >> $GITHUB_ENV
- name: Setup Golang
uses: actions/setup-go@d0a58c1c4d2b25278816e339b944508c875f3613 # v3.4.0
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
with:
go-version: ${{ env.GOLANG_VERSION }}
@@ -177,6 +177,10 @@ jobs:
run: |
set -ue
make install-codegen-tools-local
# We install kustomize in the dist directory
echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH
make manifests-local VERSION=${TARGET_VERSION}
git diff
git commit manifests/ -m "Bump version to ${TARGET_VERSION}"
@@ -201,13 +205,13 @@ jobs:
if: ${{ env.DRY_RUN != 'true' }}
- uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.1.0
- uses: docker/setup-buildx-action@8c0edbc76e98fa90f69d9a2c020dcb50019dc325 # v2.2.1
- uses: docker/setup-buildx-action@f03ac48505955848960e80bbb68046aa35c7b9e7 # v2.4.1
- name: Build and push Docker image for release
run: |
set -ue
git clean -fd
mkdir -p dist/
docker buildx build --platform linux/amd64,linux/arm64,linux/s390x,linux/ppc64le --push -t ${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION} -t argoproj/argocd:v${TARGET_VERSION} .
docker buildx build --platform linux/amd64,linux/arm64,linux/s390x,linux/ppc64le --sbom=false --provenance=false --push -t ${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION} -t argoproj/argocd:v${TARGET_VERSION} .
make release-cli
make checksums
chmod +x ./dist/argocd-linux-amd64
@@ -215,13 +219,20 @@ jobs:
if: ${{ env.DRY_RUN != 'true' }}
- name: Install cosign
uses: sigstore/cosign-installer@9becc617647dfa20ae7b1151972e9b3a2c338a2b # v2.8.1
uses: sigstore/cosign-installer@c3667d99424e7e6047999fb6246c0da843953c65 # v3.0.1
with:
cosign-release: 'v1.13.0'
cosign-release: 'v1.13.1'
- name: Install crane to get digest of image
uses: imjasonh/setup-crane@00c9e93efa4e1138c9a7a5c594acd6c75a2fbf0c
- name: Get digest of image
run: |
echo "IMAGE_DIGEST=$(crane digest quay.io/argoproj/argocd:v${TARGET_VERSION})" >> $GITHUB_ENV
- name: Sign Argo CD container images and assets
run: |
cosign sign --key env://COSIGN_PRIVATE_KEY ${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION}
cosign sign --key env://COSIGN_PRIVATE_KEY ${IMAGE_NAMESPACE}/argocd@${{ env.IMAGE_DIGEST }}
cosign sign-blob --key env://COSIGN_PRIVATE_KEY ./dist/argocd-${TARGET_VERSION}-checksums.txt > ./dist/argocd-${TARGET_VERSION}-checksums.sig
# Retrieves the public key to release as an asset
cosign public-key --key env://COSIGN_PRIVATE_KEY > ./dist/argocd-cosign.pub
@@ -264,7 +275,7 @@ jobs:
SIGS_BOM_VERSION: v0.2.1
# comma delimited list of project relative folders to inspect for package
# managers (gomod, yarn, npm).
PROJECT_FOLDERS: ".,./ui"
PROJECT_FOLDERS: ".,./ui"
# full qualified name of the docker image to be inspected
DOCKER_IMAGE: ${{env.IMAGE_NAMESPACE}}/argocd:v${{env.TARGET_VERSION}}
run: |

View File

@@ -82,6 +82,7 @@ Currently, the following organizations are **officially** using Argo CD:
1. [IITS-Consulting](https://iits-consulting.de)
1. [imaware](https://imaware.health)
1. [Index Exchange](https://www.indexexchange.com/)
1. [Info Support](https://www.infosupport.com/)
1. [InsideBoard](https://www.insideboard.com)
1. [Intuit](https://www.intuit.com/)
1. [Joblift](https://joblift.com/)

View File

@@ -1 +1 @@
2.4.18
2.4.28

View File

@@ -0,0 +1,180 @@
package controllers
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
dynfake "k8s.io/client-go/dynamic/fake"
kubefake "k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/tools/record"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"github.com/argoproj/argo-cd/v2/applicationset/generators"
argoappsetv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/applicationset/v1alpha1"
)
func TestRequeueAfter(t *testing.T) {
mockServer := argoCDServiceMock{}
ctx := context.Background()
scheme := runtime.NewScheme()
err := argoappsetv1alpha1.AddToScheme(scheme)
assert.Nil(t, err)
gvrToListKind := map[schema.GroupVersionResource]string{{
Group: "mallard.io",
Version: "v1",
Resource: "ducks",
}: "DuckList"}
appClientset := kubefake.NewSimpleClientset()
k8sClient := fake.NewClientBuilder().Build()
duckType := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v2quack",
"kind": "Duck",
"metadata": map[string]interface{}{
"name": "mightyduck",
"namespace": "namespace",
"labels": map[string]interface{}{"duck": "all-species"},
},
"status": map[string]interface{}{
"decisions": []interface{}{
map[string]interface{}{
"clusterName": "staging-01",
},
map[string]interface{}{
"clusterName": "production-01",
},
},
},
},
}
fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, duckType)
terminalGenerators := map[string]generators.Generator{
"List": generators.NewListGenerator(),
"Clusters": generators.NewClusterGenerator(k8sClient, ctx, appClientset, "argocd"),
"Git": generators.NewGitGenerator(mockServer),
"SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build()),
"ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, fakeDynClient, appClientset, "argocd"),
"PullRequest": generators.NewPullRequestGenerator(k8sClient),
}
nestedGenerators := map[string]generators.Generator{
"List": terminalGenerators["List"],
"Clusters": terminalGenerators["Clusters"],
"Git": terminalGenerators["Git"],
"SCMProvider": terminalGenerators["SCMProvider"],
"ClusterDecisionResource": terminalGenerators["ClusterDecisionResource"],
"PullRequest": terminalGenerators["PullRequest"],
"Matrix": generators.NewMatrixGenerator(terminalGenerators),
"Merge": generators.NewMergeGenerator(terminalGenerators),
}
topLevelGenerators := map[string]generators.Generator{
"List": terminalGenerators["List"],
"Clusters": terminalGenerators["Clusters"],
"Git": terminalGenerators["Git"],
"SCMProvider": terminalGenerators["SCMProvider"],
"ClusterDecisionResource": terminalGenerators["ClusterDecisionResource"],
"PullRequest": terminalGenerators["PullRequest"],
"Matrix": generators.NewMatrixGenerator(nestedGenerators),
"Merge": generators.NewMergeGenerator(nestedGenerators),
}
client := fake.NewClientBuilder().WithScheme(scheme).Build()
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Recorder: record.NewFakeRecorder(0),
Generators: topLevelGenerators,
}
type args struct {
appset *argoappsetv1alpha1.ApplicationSet
}
tests := []struct {
name string
args args
want time.Duration
wantErr assert.ErrorAssertionFunc
}{
{name: "Cluster", args: args{appset: &argoappsetv1alpha1.ApplicationSet{
Spec: argoappsetv1alpha1.ApplicationSetSpec{
Generators: []argoappsetv1alpha1.ApplicationSetGenerator{{Clusters: &argoappsetv1alpha1.ClusterGenerator{}}},
},
}}, want: generators.NoRequeueAfter, wantErr: assert.NoError},
{name: "ClusterMergeNested", args: args{&argoappsetv1alpha1.ApplicationSet{
Spec: argoappsetv1alpha1.ApplicationSetSpec{
Generators: []argoappsetv1alpha1.ApplicationSetGenerator{
{Clusters: &argoappsetv1alpha1.ClusterGenerator{}},
{Merge: &argoappsetv1alpha1.MergeGenerator{
Generators: []argoappsetv1alpha1.ApplicationSetNestedGenerator{
{
Clusters: &argoappsetv1alpha1.ClusterGenerator{},
Git: &argoappsetv1alpha1.GitGenerator{},
},
},
}},
},
},
}}, want: generators.DefaultRequeueAfterSeconds, wantErr: assert.NoError},
{name: "ClusterMatrixNested", args: args{&argoappsetv1alpha1.ApplicationSet{
Spec: argoappsetv1alpha1.ApplicationSetSpec{
Generators: []argoappsetv1alpha1.ApplicationSetGenerator{
{Clusters: &argoappsetv1alpha1.ClusterGenerator{}},
{Matrix: &argoappsetv1alpha1.MatrixGenerator{
Generators: []argoappsetv1alpha1.ApplicationSetNestedGenerator{
{
Clusters: &argoappsetv1alpha1.ClusterGenerator{},
Git: &argoappsetv1alpha1.GitGenerator{},
},
},
}},
},
},
}}, want: generators.DefaultRequeueAfterSeconds, wantErr: assert.NoError},
{name: "ListGenerator", args: args{appset: &argoappsetv1alpha1.ApplicationSet{
Spec: argoappsetv1alpha1.ApplicationSetSpec{
Generators: []argoappsetv1alpha1.ApplicationSetGenerator{{List: &argoappsetv1alpha1.ListGenerator{}}},
},
}}, want: generators.NoRequeueAfter, wantErr: assert.NoError},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, r.getMinRequeueAfter(tt.args.appset), "getMinRequeueAfter(%v)", tt.args.appset)
})
}
}
type argoCDServiceMock struct {
mock *mock.Mock
}
func (a argoCDServiceMock) GetApps(ctx context.Context, repoURL string, revision string) ([]string, error) {
args := a.mock.Called(ctx, repoURL, revision)
return args.Get(0).([]string), args.Error(1)
}
func (a argoCDServiceMock) GetFiles(ctx context.Context, repoURL string, revision string, pattern string) (map[string][]byte, error) {
args := a.mock.Called(ctx, repoURL, revision, pattern)
return args.Get(0).(map[string][]byte), args.Error(1)
}
func (a argoCDServiceMock) GetFileContent(ctx context.Context, repoURL string, revision string, path string) ([]byte, error) {
args := a.mock.Called(ctx, repoURL, revision, path)
return args.Get(0).([]byte), args.Error(1)
}
func (a argoCDServiceMock) GetDirectories(ctx context.Context, repoURL string, revision string) ([]string, error) {
args := a.mock.Called(ctx, repoURL, revision)
return args.Get(0).([]string), args.Error(1)
}

View File

@@ -51,6 +51,8 @@ func NewClusterGenerator(c client.Client, ctx context.Context, clientset kuberne
return g
}
// GetRequeueAfter never requeue the cluster generator because the `clusterSecretEventHandler` will requeue the appsets
// when the cluster secrets change
func (g *ClusterGenerator) GetRequeueAfter(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator) time.Duration {
return NoRequeueAfter
}

View File

@@ -67,28 +67,13 @@ func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.App
}
func (m *MatrixGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([]map[string]string, error) {
var matrix *argoprojiov1alpha1.MatrixGenerator
if appSetBaseGenerator.Matrix != nil {
// Since nested matrix generator is represented as a JSON object in the CRD, we unmarshall it back to a Go struct here.
nestedMatrix, err := argoprojiov1alpha1.ToNestedMatrixGenerator(appSetBaseGenerator.Matrix)
if err != nil {
return nil, fmt.Errorf("unable to unmarshall nested matrix generator: %v", err)
}
if nestedMatrix != nil {
matrix = nestedMatrix.ToMatrixGenerator()
}
matrixGen, err := getMatrixGenerator(appSetBaseGenerator)
if err != nil {
return nil, err
}
var mergeGenerator *argoprojiov1alpha1.MergeGenerator
if appSetBaseGenerator.Merge != nil {
// Since nested merge generator is represented as a JSON object in the CRD, we unmarshall it back to a Go struct here.
nestedMerge, err := argoprojiov1alpha1.ToNestedMergeGenerator(appSetBaseGenerator.Merge)
if err != nil {
return nil, fmt.Errorf("unable to unmarshall nested merge generator: %v", err)
}
if nestedMerge != nil {
mergeGenerator = nestedMerge.ToMergeGenerator()
}
mergeGen, err := getMergeGenerator(appSetBaseGenerator)
if err != nil {
return nil, err
}
t, err := Transform(
@@ -99,8 +84,8 @@ func (m *MatrixGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.Appli
SCMProvider: appSetBaseGenerator.SCMProvider,
ClusterDecisionResource: appSetBaseGenerator.ClusterDecisionResource,
PullRequest: appSetBaseGenerator.PullRequest,
Matrix: matrix,
Merge: mergeGenerator,
Matrix: matrixGen,
Merge: mergeGen,
},
m.supportedGenerators,
argoprojiov1alpha1.ApplicationSetTemplate{},
@@ -128,10 +113,15 @@ func (m *MatrixGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.Ap
var found bool
for _, r := range appSetGenerator.Matrix.Generators {
matrixGen, _ := getMatrixGenerator(r)
mergeGen, _ := getMergeGenerator(r)
base := &argoprojiov1alpha1.ApplicationSetGenerator{
List: r.List,
Clusters: r.Clusters,
Git: r.Git,
List: r.List,
Clusters: r.Clusters,
Git: r.Git,
PullRequest: r.PullRequest,
Matrix: matrixGen,
Merge: mergeGen,
}
generators := GetRelevantGenerators(base, m.supportedGenerators)
@@ -152,6 +142,17 @@ func (m *MatrixGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.Ap
}
func getMatrixGenerator(r argoprojiov1alpha1.ApplicationSetNestedGenerator) (*argoprojiov1alpha1.MatrixGenerator, error) {
if r.Matrix == nil {
return nil, nil
}
matrix, err := argoprojiov1alpha1.ToNestedMatrixGenerator(r.Matrix)
if err != nil {
return nil, err
}
return matrix.ToMatrixGenerator(), nil
}
func (m *MatrixGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
return &appSetGenerator.Matrix.Template
}

View File

@@ -191,6 +191,8 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
Elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "Cluster","url": "Url"}`)}},
}
pullRequestGenerator := &argoprojiov1alpha1.PullRequestGenerator{}
testCases := []struct {
name string
baseGenerators []argoprojiov1alpha1.ApplicationSetNestedGenerator
@@ -223,6 +225,31 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
gitGetRequeueAfter: time.Duration(1),
expected: time.Duration(1),
},
{
name: "returns the minimal time for pull request",
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
{
Git: gitGenerator,
},
{
PullRequest: pullRequestGenerator,
},
},
gitGetRequeueAfter: time.Duration(15 * time.Second),
expected: time.Duration(15 * time.Second),
},
{
name: "returns the default time if no requeueAfterSeconds is provided",
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
{
Git: gitGenerator,
},
{
PullRequest: pullRequestGenerator,
},
},
expected: time.Duration(30 * time.Minute),
},
}
for _, testCase := range testCases {
@@ -233,16 +260,18 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
for _, g := range testCaseCopy.baseGenerators {
gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{
Git: g.Git,
List: g.List,
Git: g.Git,
List: g.List,
PullRequest: g.PullRequest,
}
mock.On("GetRequeueAfter", &gitGeneratorSpec).Return(testCaseCopy.gitGetRequeueAfter, nil)
}
var matrixGenerator = NewMatrixGenerator(
map[string]Generator{
"Git": mock,
"List": &ListGenerator{},
"Git": mock,
"List": &ListGenerator{},
"PullRequest": &PullRequestGenerator{},
},
)

View File

@@ -127,27 +127,13 @@ func getParamSetsByMergeKey(mergeKeys []string, paramSets []map[string]string) (
// getParams get the parameters generated by this generator.
func (m *MergeGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([]map[string]string, error) {
var matrix *argoprojiov1alpha1.MatrixGenerator
if appSetBaseGenerator.Matrix != nil {
nestedMatrix, err := argoprojiov1alpha1.ToNestedMatrixGenerator(appSetBaseGenerator.Matrix)
if err != nil {
return nil, err
}
if nestedMatrix != nil {
matrix = nestedMatrix.ToMatrixGenerator()
}
matrixGen, err := getMatrixGenerator(appSetBaseGenerator)
if err != nil {
return nil, err
}
var mergeGenerator *argoprojiov1alpha1.MergeGenerator
if appSetBaseGenerator.Merge != nil {
nestedMerge, err := argoprojiov1alpha1.ToNestedMergeGenerator(appSetBaseGenerator.Merge)
if err != nil {
return nil, err
}
if nestedMerge != nil {
mergeGenerator = nestedMerge.ToMergeGenerator()
}
mergeGen, err := getMergeGenerator(appSetBaseGenerator)
if err != nil {
return nil, err
}
t, err := Transform(
@@ -158,8 +144,8 @@ func (m *MergeGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.Applic
SCMProvider: appSetBaseGenerator.SCMProvider,
ClusterDecisionResource: appSetBaseGenerator.ClusterDecisionResource,
PullRequest: appSetBaseGenerator.PullRequest,
Matrix: matrix,
Merge: mergeGenerator,
Matrix: matrixGen,
Merge: mergeGen,
},
m.supportedGenerators,
argoprojiov1alpha1.ApplicationSetTemplate{},
@@ -185,10 +171,15 @@ func (m *MergeGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.App
var found bool
for _, r := range appSetGenerator.Merge.Generators {
matrixGen, _ := getMatrixGenerator(r)
mergeGen, _ := getMergeGenerator(r)
base := &argoprojiov1alpha1.ApplicationSetGenerator{
List: r.List,
Clusters: r.Clusters,
Git: r.Git,
List: r.List,
Clusters: r.Clusters,
Git: r.Git,
PullRequest: r.PullRequest,
Matrix: matrixGen,
Merge: mergeGen,
}
generators := GetRelevantGenerators(base, m.supportedGenerators)
@@ -209,6 +200,17 @@ func (m *MergeGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.App
}
func getMergeGenerator(r argoprojiov1alpha1.ApplicationSetNestedGenerator) (*argoprojiov1alpha1.MergeGenerator, error) {
if r.Merge == nil {
return nil, nil
}
merge, err := argoprojiov1alpha1.ToNestedMergeGenerator(r.Merge)
if err != nil {
return nil, err
}
return merge.ToMergeGenerator(), nil
}
// GetTemplate gets the Template field for the MergeGenerator.
func (m *MergeGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
return &appSetGenerator.Merge.Template

View File

@@ -265,6 +265,16 @@
"description": "the repoURL to restrict returned list applications.",
"name": "repo",
"in": "query"
},
{
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "multi",
"description": "the project names to restrict returned list applications (legacy name for backwards-compatibility).",
"name": "project",
"in": "query"
}
],
"responses": {
@@ -529,6 +539,16 @@
"description": "the repoURL to restrict returned list applications.",
"name": "repo",
"in": "query"
},
{
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "multi",
"description": "the project names to restrict returned list applications (legacy name for backwards-compatibility).",
"name": "project",
"in": "query"
}
],
"responses": {
@@ -3167,6 +3187,16 @@
"description": "the repoURL to restrict returned list applications.",
"name": "repo",
"in": "query"
},
{
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "multi",
"description": "the project names to restrict returned list applications (legacy name for backwards-compatibility).",
"name": "project",
"in": "query"
}
],
"responses": {

View File

@@ -207,7 +207,7 @@ var validatorsByGroup = map[string]settingValidator{
}
ssoProvider = "Dex"
} else if general.OIDCConfigRAW != "" {
if _, err := settings.UnmarshalOIDCConfig(general.OIDCConfigRAW); err != nil {
if err := settings.ValidateOIDCConfig(general.OIDCConfigRAW); err != nil {
return "", fmt.Errorf("invalid oidc.config: %v", err)
}
ssoProvider = "OIDC"

View File

@@ -8,8 +8,10 @@ import (
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"time"
"unicode"
"github.com/argoproj/pkg/rand"
@@ -73,9 +75,8 @@ func runCommand(ctx context.Context, command Command, path string, env []string)
}
logCtx := log.WithFields(log.Fields{"execID": execId})
// log in a way we can copy-and-paste into a terminal
args := strings.Join(cmd.Args, " ")
logCtx.WithFields(log.Fields{"dir": cmd.Dir}).Info(args)
argsToLog := getCommandArgsToLog(cmd)
logCtx.WithFields(log.Fields{"dir": cmd.Dir}).Info(argsToLog)
var stdout bytes.Buffer
var stderr bytes.Buffer
@@ -106,7 +107,7 @@ func runCommand(ctx context.Context, command Command, path string, env []string)
logCtx.WithFields(log.Fields{"duration": duration}).Debug(output)
if err != nil {
err := newCmdError(args, errors.New(err.Error()), strings.TrimSpace(stderr.String()))
err := newCmdError(argsToLog, errors.New(err.Error()), strings.TrimSpace(stderr.String()))
logCtx.Error(err.Error())
return strings.TrimSuffix(output, "\n"), err
}
@@ -114,6 +115,28 @@ func runCommand(ctx context.Context, command Command, path string, env []string)
return strings.TrimSuffix(output, "\n"), nil
}
// getCommandArgsToLog represents the given command in a way that we can copy-and-paste into a terminal
func getCommandArgsToLog(cmd *exec.Cmd) string {
var argsToLog []string
for _, arg := range cmd.Args {
containsSpace := false
for _, r := range arg {
if unicode.IsSpace(r) {
containsSpace = true
break
}
}
if containsSpace {
// add quotes and escape any internal quotes
argsToLog = append(argsToLog, strconv.Quote(arg))
} else {
argsToLog = append(argsToLog, arg)
}
}
args := strings.Join(argsToLog, " ")
return args
}
type CmdError struct {
Args string
Stderr string

View File

@@ -2,6 +2,7 @@ package plugin
import (
"context"
"os/exec"
"path/filepath"
"testing"
"time"
@@ -266,3 +267,30 @@ func TestRunCommandContextTimeout(t *testing.T) {
assert.Error(t, err) // The command should time out, causing an error.
assert.Less(t, after.Sub(before), 1*time.Second)
}
func Test_getCommandArgsToLog(t *testing.T) {
testCases := []struct {
name string
args []string
expected string
}{
{
name: "no spaces",
args: []string{"sh", "-c", "cat"},
expected: "sh -c cat",
},
{
name: "spaces",
args: []string{"sh", "-c", `echo "hello world"`},
expected: `sh -c "echo \"hello world\""`,
},
}
for _, tc := range testCases {
tcc := tc
t.Run(tcc.name, func(t *testing.T) {
t.Parallel()
assert.Equal(t, tcc.expected, getCommandArgsToLog(exec.Command(tcc.args[0], tcc.args[1:]...)))
})
}
}

View File

@@ -1,12 +1,15 @@
package common
import (
"errors"
"os"
"path/filepath"
"strconv"
"time"
"github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// Default service addresses and URLS of Argo CD internal services
@@ -293,3 +296,10 @@ const (
// Keep alive is 2x enforcement minimum to ensure network jitter does not introduce ENHANCE_YOUR_CALM errors
GRPCKeepAliveTime = 2 * GRPCKeepAliveEnforcementMinimum
)
// Common error messages
const TokenVerificationError = "failed to verify the token"
var TokenVerificationErr = errors.New(TokenVerificationError)
var PermissionDeniedAPIError = status.Error(codes.PermissionDenied, "permission denied")

View File

@@ -25,6 +25,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"github.com/argoproj/argo-cd/v2/controller/metrics"
@@ -389,6 +390,20 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e
return nil, fmt.Errorf("controller is configured to ignore cluster %s", cluster.Server)
}
clusterCacheConfig := cluster.RESTConfig()
// Controller dynamically fetches all resource types available on the cluster
// using a discovery API that may contain deprecated APIs.
// This causes log flooding when managing a large number of clusters.
// https://github.com/argoproj/argo-cd/issues/11973
// However, we can safely suppress deprecation warnings
// because we do not rely on resources with a particular API group or version.
// https://kubernetes.io/blog/2020/09/03/warnings/#customize-client-handling
//
// Completely suppress warning logs only for log levels that are less than Debug.
if log.GetLevel() < log.DebugLevel {
clusterCacheConfig.WarningHandler = rest.NoWarnings{}
}
clusterCacheOpts := []clustercache.UpdateSettingsFunc{
clustercache.SetListSemaphore(semaphore.NewWeighted(clusterCacheListSemaphoreSize)),
clustercache.SetListPageSize(clusterCacheListPageSize),
@@ -420,7 +435,7 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e
clustercache.SetRetryOptions(clusterCacheAttemptLimit, clusterCacheRetryUseBackoff, isRetryableError),
}
clusterCache = clustercache.NewClusterCache(cluster.RESTConfig(), clusterCacheOpts...)
clusterCache = clustercache.NewClusterCache(clusterCacheConfig, clusterCacheOpts...)
_ = clusterCache.OnResourceUpdated(func(newRes *clustercache.Resource, oldRes *clustercache.Resource, namespaceResources map[kube.ResourceKey]*clustercache.Resource) {
toNotify := make(map[string]bool)

View File

@@ -9,16 +9,6 @@ setTimeout(function() {
caret.innerHTML = "<i class='fa fa-caret-down dropdown-caret'></i>"
caret.classList.add('dropdown-caret')
div.querySelector('.rst-current-version').appendChild(caret);
div.querySelector('.rst-current-version').addEventListener('click', function() {
const classes = container.className.split(' ');
const index = classes.indexOf('shift-up');
if (index === -1) {
classes.push('shift-up');
} else {
classes.splice(index, 1);
}
container.className = classes.join(' ');
});
}
var CSSLink = document.createElement('link');

View File

@@ -122,9 +122,9 @@ To terminate the sync, click on the "synchronisation" then "terminate":
![Synchronization](assets/synchronization-button.png) ![Terminate](assets/terminate-button.png)
## Why Is My App Out Of Sync Even After Syncing?
## Why Is My App `Out Of Sync` Even After Syncing?
Is some cases, the tool you use may conflict with Argo CD by adding the `app.kubernetes.io/instance` label. E.g. using
In some cases, the tool you use may conflict with Argo CD by adding the `app.kubernetes.io/instance` label. E.g. using
Kustomize common labels feature.
Argo CD automatically sets the `app.kubernetes.io/instance` label and uses it to determine which resources form the app.
@@ -151,7 +151,7 @@ E.g.
To fix this use diffing
customizations [settings](./user-guide/diffing.md#known-kubernetes-types-in-crds-resource-limits-volume-mounts-etc).
## How Do I Fix "invalid cookie, longer than max length 4093"?
## How Do I Fix `invalid cookie, longer than max length 4093`?
Argo CD uses a JWT as the auth token. You likely are part of many groups and have gone over the 4KB limit which is set
for cookies. You can get the list of groups by opening "developer tools -> network"
@@ -218,4 +218,38 @@ resource.customizations.health.bitnami.com_SealedSecret: |
hs.status = "Healthy"
hs.message = "Controller doesn't report resource status"
return hs
```
```
## How do I fix `The order in patch list … doesn't match $setElementOrder list: …`?
An application may trigger a sync error labeled a `ComparisonError` with a message like:
> The order in patch list: [map[name:**KEY_BC** value:150] map[name:**KEY_BC** value:500] map[name:**KEY_BD** value:250] map[name:**KEY_BD** value:500] map[name:KEY_BI value:something]] doesn't match $setElementOrder list: [map[name:KEY_AA] map[name:KEY_AB] map[name:KEY_AC] map[name:KEY_AD] map[name:KEY_AE] map[name:KEY_AF] map[name:KEY_AG] map[name:KEY_AH] map[name:KEY_AI] map[name:KEY_AJ] map[name:KEY_AK] map[name:KEY_AL] map[name:KEY_AM] map[name:KEY_AN] map[name:KEY_AO] map[name:KEY_AP] map[name:KEY_AQ] map[name:KEY_AR] map[name:KEY_AS] map[name:KEY_AT] map[name:KEY_AU] map[name:KEY_AV] map[name:KEY_AW] map[name:KEY_AX] map[name:KEY_AY] map[name:KEY_AZ] map[name:KEY_BA] map[name:KEY_BB] map[name:**KEY_BC**] map[name:**KEY_BD**] map[name:KEY_BE] map[name:KEY_BF] map[name:KEY_BG] map[name:KEY_BH] map[name:KEY_BI] map[name:**KEY_BC**] map[name:**KEY_BD**]]
There are two parts to the message:
1. `The order in patch list: [`
This identifies values for items, especially items that appear multiple times:
> map[name:**KEY_BC** value:150] map[name:**KEY_BC** value:500] map[name:**KEY_BD** value:250] map[name:**KEY_BD** value:500] map[name:KEY_BI value:something]
You'll want to identify the keys that are duplicated -- you can focus on the first part, as each duplicated key will appear, once for each of its value with its value in the first list. The second list is really just
`]`
2. `doesn't match $setElementOrder list: [`
This includes all of the keys. It's included for debugging purposes -- you don't need to pay much attention to it. It will give you a hint about the precise location in the list for the duplicated keys:
> map[name:KEY_AA] map[name:KEY_AB] map[name:KEY_AC] map[name:KEY_AD] map[name:KEY_AE] map[name:KEY_AF] map[name:KEY_AG] map[name:KEY_AH] map[name:KEY_AI] map[name:KEY_AJ] map[name:KEY_AK] map[name:KEY_AL] map[name:KEY_AM] map[name:KEY_AN] map[name:KEY_AO] map[name:KEY_AP] map[name:KEY_AQ] map[name:KEY_AR] map[name:KEY_AS] map[name:KEY_AT] map[name:KEY_AU] map[name:KEY_AV] map[name:KEY_AW] map[name:KEY_AX] map[name:KEY_AY] map[name:KEY_AZ] map[name:KEY_BA] map[name:KEY_BB] map[name:**KEY_BC**] map[name:**KEY_BD**] map[name:KEY_BE] map[name:KEY_BF] map[name:KEY_BG] map[name:KEY_BH] map[name:KEY_BI] map[name:**KEY_BC**] map[name:**KEY_BD**]
`]`
In this case, the duplicated keys have been **emphasized** to help you identify the problematic keys. Many editors have the ability to highlight all instances of a string, using such an editor can help with such problems.
The most common instance of this error is with `env:` fields for `containers`.
!!! note "Dynamic applications"
It's possible that your application is being generated by a tool in which case the duplication might not be evident within the scope of a single file. If you have trouble debugging this problem, consider filing a ticket to the owner of the generator tool asking them to improve its validation and error reporting.

View File

@@ -125,7 +125,10 @@ spec:
# Destination cluster and namespace to deploy the application
destination:
# cluster API URL
server: https://kubernetes.default.svc
# or cluster name
# name: in-cluster
# The namespace will only be set for namespace-scoped resources that have not set a value for .metadata.namespace
namespace: guestbook

View File

@@ -58,7 +58,7 @@ spec:
* `repo`: Required name of the GitHub repository.
* `api`: If using GitHub Enterprise, the URL to access it. (Optional)
* `tokenRef`: A `Secret` name and key containing the GitHub access token to use for requests. If not specified, will make anonymous requests which have a lower rate limit and can only see public repositories. (Optional)
* `labels`: Labels is used to filter the PRs that you want to target. (Optional)
* `labels`: Filter the PRs to those containing **all** of the labels listed. (Optional)
## Gitea

View File

@@ -47,7 +47,7 @@ data:
help.download.windows-amd64: "path-or-url-to-download"
# A dex connector configuration (optional). See SSO configuration documentation:
# https://github.com/argoproj/argo-cd/blob/master/docs/operator-manual/sso
# https://github.com/argoproj/argo-cd/blob/master/docs/operator-manual/user-management/index.md#sso
# https://dexidp.io/docs/connectors/
dex.config: |
connectors:

View File

@@ -51,7 +51,7 @@ following example builds an entirely customized repo-server from a Dockerfile, i
dependencies that may be needed for generating manifests.
```Dockerfile
FROM argoproj/argocd:latest
FROM argoproj/argocd:v2.5.4 # Replace tag with the appropriate argo version
# Switch to root for the ability to perform install
USER root

View File

@@ -77,7 +77,7 @@ spec:
```
!!! warning
By default, deleting an application will not perform a cascade delete, which would delete its resources. You must add the finalizer if you want this behaviour - which you may well not want.
Without the `resources-finalizer.argocd.argoproj.io` finalizer, deleting an application will not delete the resources it manages. To perform a cascading delete, you must add the finalizer. See [App Deletion](../user-guide/app_deletion.md#about-the-deletion-finalizer).
```yaml
metadata:

View File

@@ -5,7 +5,7 @@ Argo CD provides built-in health assessment for several standard Kubernetes type
surfaced to the overall Application health status as a whole. The following checks are made for
specific types of kubernetes resources:
### Deployment, ReplicaSet, StatefulSet DaemonSet
### Deployment, ReplicaSet, StatefulSet, DaemonSet
* Observed generation is equal to desired generation.
* Number of **updated** replicas equals the number of desired replicas.

View File

@@ -1,10 +1,8 @@
# High Availability
Argo CD is largely stateless, all data is persisted as Kubernetes objects, which in turn is stored in Kubernetes' etcd. Redis is only used as a throw-away cache and can be lost. When lost, it will be rebuilt without loss of service.
Argo CD is largely stateless. All data is persisted as Kubernetes objects, which in turn is stored in Kubernetes' etcd. Redis is only used as a throw-away cache and can be lost. When lost, it will be rebuilt without loss of service.
A set of HA manifests are provided for users who wish to run Argo CD in a highly available manner. This runs more containers, and runs Redis in HA mode.
[Manifests ⧉](https://github.com/argoproj/argo-cd/tree/master/manifests)
A set of [HA manifests](https://github.com/argoproj/argo-cd/tree/master/manifests/ha) are provided for users who wish to run Argo CD in a highly available manner. This runs more containers, and runs Redis in HA mode.
!!! note
The HA installation will require at least three different nodes due to pod anti-affinity roles in the specs.
@@ -17,11 +15,11 @@ A set of HA manifests are provided for users who wish to run Argo CD in a highly
The `argocd-repo-server` is responsible for cloning Git repository, keeping it up to date and generating manifests using the appropriate tool.
* `argocd-repo-server` fork/exec config management tool to generate manifests. The fork can fail due to lack of memory and limit on the number of OS threads.
The `--parallelismlimit` flag controls how many manifests generations are running concurrently and allows avoiding OOM kills.
* `argocd-repo-server` fork/exec config management tool to generate manifests. The fork can fail due to lack of memory or limit on the number of OS threads.
The `--parallelismlimit` flag controls how many manifests generations are running concurrently and helps avoid OOM kills.
* the `argocd-repo-server` ensures that repository is in the clean state during the manifest generation using config management tools such as Kustomize, Helm
or custom plugin. As a result Git repositories with multiple applications might be affect repository server performance.
or custom plugin. As a result Git repositories with multiple applications might affect repository server performance.
Read [Monorepo Scaling Considerations](#monorepo-scaling-considerations) for more information.
* `argocd-repo-server` clones repository into `/tmp` ( of path specified in `TMPDIR` env variable ). Pod might run out of disk space if have too many repository
@@ -30,7 +28,7 @@ or repositories has a lot of files. To avoid this problem mount persistent volum
* `argocd-repo-server` `git ls-remote` to resolve ambiguous revision such as `HEAD`, branch or tag name. This operation is happening pretty frequently
and might fail. To avoid failed syncs use `ARGOCD_GIT_ATTEMPTS_COUNT` environment variable to retry failed requests.
* `argocd-repo-server` Every 3m (by default) Argo CD checks for changes to the app manifests. Argo CD assumes by default that manifests only change when the repo changes, so it caches generated manifests (for 24h by default). With Kustomize remote bases, or Helm patch releases, the manifests can change even though the repo has not changed. By reducing the cache time, you can get the changes without waiting for 24h. Use `--repo-cache-expiration duration`, and we'd suggest in low volume environments you try '1h'. Bear in mind this will negate the benefit of caching if set too low.
* `argocd-repo-server` Every 3m (by default) Argo CD checks for changes to the app manifests. Argo CD assumes by default that manifests only change when the repo changes, so it caches the generated manifests (for 24h by default). With Kustomize remote bases, or Helm patch releases, the manifests can change even though the repo has not changed. By reducing the cache time, you can get the changes without waiting for 24h. Use `--repo-cache-expiration duration`, and we'd suggest in low volume environments you try '1h'. Bear in mind that this will negate the benefits of caching if set too low.
* `argocd-repo-server` fork exec config management tools such as `helm` or `kustomize` and enforces 90 seconds timeout. The timeout can be increased using `ARGOCD_EXEC_TIMEOUT` env variable. The value should be in Go time duration string format, for example, `2m30s`.

View File

@@ -74,7 +74,7 @@ kind: Kustomization
namespace: argocd
resources:
- https://raw.githubusercontent.com/argoproj/argo-cd/v2.0.4/manifests/ha/install.yaml
- github.com/argoproj/argo-cd/manifests/ha?ref=v2.6.2
```
## Helm

View File

@@ -15,9 +15,11 @@ spec:
- '*'
# Only permit applications to deploy to the guestbook namespace in the same cluster
# Destination clusters can be identified by 'server', 'name', or both.
destinations:
- namespace: guestbook
server: https://kubernetes.default.svc
name: in-cluster
# Deny all cluster-scoped resources from being created, except for Namespace
clusterResourceWhitelist:

View File

@@ -9,9 +9,8 @@ Operators can add actions to custom resources in form of a Lua script and expand
Argo CD supports custom resource actions written in [Lua](https://www.lua.org/). This is useful if you:
* Have a custom resource for which Argo CD does not provide any built-in actions.
* Have a commonly performed manual task that might be error prone if executed by users via `kubectl`
* Have a custom resource for which Argo CD does not provide any built-in actions.
* Have a commonly performed manual task that might be error prone if executed by users via `kubectl`
You can define your own custom resource actions in the `argocd-cm` ConfigMap.

View File

@@ -1,6 +1,11 @@
# Secret Management
Argo CD is un-opinionated about how secrets are managed. There's many ways to do it and there's no one-size-fits-all solution. Here's some ways people are doing GitOps secrets:
Argo CD is un-opinionated about how secrets are managed. There are many ways to do it, and there's no one-size-fits-all solution.
Many solutions use plugins to inject secrets into the application manifests. See [Mitigating Risks of Secret-Injection Plugins](#mitigating-risks-of-secret-injection-plugins)
below to make sure you use those plugins securely.
Here are some ways people are doing GitOps secrets:
* [Bitnami Sealed Secrets](https://github.com/bitnami-labs/sealed-secrets)
* [GoDaddy Kubernetes External Secrets](https://github.com/godaddy/kubernetes-external-secrets)
@@ -15,3 +20,17 @@ Argo CD is un-opinionated about how secrets are managed. There's many ways to do
* [argocd-vault-replacer](https://github.com/crumbhole/argocd-vault-replacer)
For discussion, see [#1364](https://github.com/argoproj/argo-cd/issues/1364)
## Mitigating Risks of Secret-Injection Plugins
Argo CD caches the manifests generated by plugins, along with the injected secrets, in its Redis instance. Those
manifests are also available via the repo-server API (a gRPC service). This means that the secrets are available to
anyone who has access to the Redis instance or to the repo-server.
Consider these steps to mitigate the risks of secret-injection plugins:
1. Set up network policies to prevent direct access to Argo CD components (Redis and the repo-server). Make sure your
cluster supports those network policies and can actually enforce them.
2. Consider running Argo CD on its own cluster, with no other applications running on it.
3. [Enable password authentication on the Redis instance](https://github.com/argoproj/argo-cd/issues/3130) (currently
only supported for non-HA Argo CD installations).

View File

@@ -1,4 +1,4 @@
# v1.8 to v2.0
# v1.8 to 2.0
## Redis Upgraded to v6.2.1

View File

@@ -1,5 +1,26 @@
# v2.3 to 2.4
## Known Issues
### Broken `project` filter before 2.4.27
Argo CD 2.4.0 introduced a breaking API change, renaming the `project` filter to `projects`.
#### Impact to API clients
A similar issue applies to other API clients which communicate with the Argo CD API server via its REST API. If the
client uses the `project` field to filter projects, the filter will not be applied. **The failing project filter could
have detrimental consequences if, for example, you rely on it to list Applications to be deleted.**
#### Impact to CLI clients
CLI clients older that v2.4.0 rely on client-side filtering and are not impacted by this bug.
#### How to fix the problem
Upgrade to Argo CD >=2.4.27, >=2.5.15, or >=2.6.6. This version of Argo CD will accept both `project` and `projects` as
valid filters.
## KSonnet support is removed
Ksonnet was deprecated in [2019](https://github.com/ksonnet/ksonnet/pull/914/files) and is no longer maintained.

View File

@@ -37,7 +37,6 @@ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/<v
<hr/>
* [v2.4 to v2.5](./2.4-2.5.md)
* [v2.3 to v2.4](./2.3-2.4.md)
* [v2.2 to v2.3](./2.2-2.3.md)
* [v2.1 to v2.2](./2.1-2.2.md)

View File

@@ -301,6 +301,19 @@ data:
issuer: https://dev-123456.oktapreview.com
clientID: aaaabbbbccccddddeee
clientSecret: $oidc.okta.clientSecret
# Optional list of allowed aud claims. If omitted or empty, defaults to the clientID value above (and the
# cliCientID, if that is also specified). If you specify a list and want the clientID to be allowed, you must
# explicitly include it in the list.
# Token verification will pass if any of the token's audiences matches any of the audiences in this list.
allowedAudiences:
- aaaabbbbccccddddeee
- qqqqwwwweeeerrrrttt
# Optional. If false, tokens without an audience will always fail validation. If true, tokens without an audience
# will always pass validation.
# Defaults to true for Argo CD < 2.6.0. Defaults to false for Argo CD >= 2.6.0.
skipAudienceCheckWhenTokenHasNoAudience: true
# Optional set of OIDC scopes to request. If omitted, defaults to: ["openid", "profile", "email", "groups"]
requestedScopes: ["openid", "profile", "email", "groups"]

View File

@@ -12,5 +12,6 @@ Before effectively using Argo CD, it is necessary to understand the underlying t
* Depending on how you plan to template your applications:
* [Kustomize](https://kustomize.io)
* [Helm](https://helm.sh)
* If you're integrating with Jenkins:
* [Jenkins User Guide](https://jenkins.io)
* If you're integrating with a CI tool:
* [GitHub Actions Documentation](https://docs.github.com/en/actions)
* [Jenkins User Guide](https://www.jenkins.io/doc/book/)

View File

@@ -24,9 +24,10 @@ argocd app delete APPNAME
# Deletion Using `kubectl`
To perform a non-cascade delete:
To perform a non-cascade delete, make sure the finalizer is unset and then delete the app:
```bash
kubectl patch app APPNAME -p '{"metadata": {"finalizers": null}}' --type merge
kubectl delete app APPNAME
```

View File

@@ -43,6 +43,9 @@ spec:
recurse: true
```
!!! warning
Directory-type applications only work for plain manifest files. If Argo CD encounters Kustomize, Helm, or Jsonnet files when directory: is set, it will fail to render the manifests.
## Including/Excluding Files
### Including Only Certain Files

View File

@@ -283,7 +283,7 @@ Helm, [starting with v3.6.1](https://github.com/helm/helm/releases/tag/v3.6.1),
prevents sending repository credentials to download charts that are being served
from a different domain than the repository.
If needed, it is possible to specifically set the Helm version to template with by setting the `helm-pass-credentials` flag on the cli:
If needed, it is possible to opt into passing credentials for all domains by setting the `helm-pass-credentials` flag on the cli:
```bash
argocd app set helm-guestbook --helm-pass-credentials

View File

@@ -69,7 +69,7 @@ spec:
source:
repoURL: https://github.com/argoproj/argocd-example-apps.git
targetRevision: HEAD
path: guestbook-kustomize
path: kustomize-guestbook
kustomize:
version: v3.5.4

23
go.mod
View File

@@ -61,7 +61,7 @@ require (
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1
github.com/pquerna/cachecontrol v0.1.0 // indirect
github.com/prometheus/client_golang v1.11.0
github.com/prometheus/client_golang v1.11.1
github.com/r3labs/diff v1.1.0
github.com/robfig/cron v1.2.0
github.com/rs/cors v1.8.0 // indirect
@@ -76,10 +76,10 @@ require (
github.com/xanzy/go-gitlab v0.60.0
github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
golang.org/x/net v0.0.0-20220621193019-9d032be2e588
golang.org/x/net v0.7.0
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
golang.org/x/term v0.5.0
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa
google.golang.org/grpc v1.45.0
google.golang.org/protobuf v1.28.0
@@ -201,11 +201,11 @@ require (
go.mongodb.org/mongo-driver v1.1.2 // indirect
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
golang.org/x/exp v0.0.0-20210901193431-a062eea981d2 // indirect
golang.org/x/mod v0.5.1-0.20210830214625-1b1db11ec8f4 // indirect
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff // indirect
golang.org/x/tools v0.1.12 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gomodules.xyz/envconfig v1.3.1-0.20190308184047-426f31af0d45 // indirect
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
@@ -214,7 +214,7 @@ require (
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
k8s.io/apiserver v0.23.1
@@ -249,6 +249,8 @@ require (
)
replace (
// Address CVE-2021-4238
github.com/Masterminds/goutils => github.com/Masterminds/goutils v1.1.1
// https://github.com/golang/go/issues/33546#issuecomment-519656923
github.com/go-check/check => github.com/go-check/check v0.0.0-20180628173108-788fd7840127
@@ -257,6 +259,9 @@ replace (
github.com/grpc-ecosystem/grpc-gateway => github.com/grpc-ecosystem/grpc-gateway v1.16.0
github.com/improbable-eng/grpc-web => github.com/improbable-eng/grpc-web v0.0.0-20181111100011-16092bd1d58a
// Avoid CVE-2022-3064
gopkg.in/yaml.v2 => gopkg.in/yaml.v2 v2.2.4
// Avoid CVE-2022-28948
gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1

40
go.sum
View File

@@ -87,8 +87,8 @@ github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU=
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg=
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
@@ -954,8 +954,9 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s=
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@@ -1282,8 +1283,9 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.5.1-0.20210830214625-1b1db11ec8f4 h1:7Qds88gNaRx0Dz/1wOwXlR7asekh1B1u26wEwN6FcEI=
golang.org/x/mod v0.5.1-0.20210830214625-1b1db11ec8f4/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1346,8 +1348,8 @@ golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220621193019-9d032be2e588 h1:9ubFuySsnAJYGyJrZ3koiEv8FyqofCBdz3G9Mbf2YFc=
golang.org/x/net v0.0.0-20220621193019-9d032be2e588/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1380,8 +1382,9 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1491,12 +1494,13 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1505,8 +1509,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1588,8 +1593,9 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff h1:VX/uD7MK0AHXGiScH3fsieUQUcpmRERPDYtqZdJnA+Q=
golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1804,16 +1810,8 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=

View File

@@ -35,7 +35,7 @@ spec:
runAsNonRoot: true
containers:
- name: dex
image: ghcr.io/dexidp/dex:v2.35.3
image: ghcr.io/dexidp/dex:v2.36.0
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.4.18
newTag: v2.4.28
resources:
- ./application-controller
- ./dex

View File

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

View File

@@ -9384,7 +9384,7 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -9464,7 +9464,7 @@ spec:
- ""
- --appendonly
- "no"
image: redis:7.0.4-alpine
image: redis:7.0.8-alpine
imagePullPolicy: Always
name: redis
ports:
@@ -9614,7 +9614,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -9663,7 +9663,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -9850,7 +9850,7 @@ spec:
key: otlp.address
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -12,4 +12,4 @@ resources:
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v2.4.18
newTag: v2.4.28

View File

@@ -11,7 +11,7 @@ patchesStrategicMerge:
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v2.4.18
newTag: v2.4.28
resources:
- ../../base/application-controller
- ../../base/applicationset-controller

View File

@@ -770,7 +770,7 @@ spec:
topologyKey: kubernetes.io/hostname
initContainers:
- name: config-init
image: haproxy:2.0.29-alpine
image: haproxy:2.0.31-alpine
imagePullPolicy: IfNotPresent
resources:
{}
@@ -790,7 +790,7 @@ spec:
runAsUser: 1000
containers:
- name: haproxy
image: haproxy:2.0.29-alpine
image: haproxy:2.0.31-alpine
imagePullPolicy: IfNotPresent
livenessProbe:
httpGet:
@@ -878,7 +878,7 @@ spec:
automountServiceAccountToken: false
initContainers:
- name: config-init
image: redis:7.0.4-alpine
image: redis:7.0.8-alpine
imagePullPolicy: IfNotPresent
resources:
{}
@@ -906,7 +906,7 @@ spec:
containers:
- name: redis
image: redis:7.0.4-alpine
image: redis:7.0.8-alpine
imagePullPolicy: IfNotPresent
command:
- redis-server
@@ -947,7 +947,7 @@ spec:
lifecycle:
{}
- name: sentinel
image: redis:7.0.4-alpine
image: redis:7.0.8-alpine
imagePullPolicy: IfNotPresent
command:
- redis-sentinel

View File

@@ -9,12 +9,12 @@ redis-ha:
haproxy:
enabled: true
image:
tag: 2.0.29-alpine
tag: 2.0.31-alpine
timeout:
server: 6m
client: 6m
checkInterval: 3s
image:
tag: 7.0.4-alpine
tag: 7.0.8-alpine
sentinel:
bind: "0.0.0.0"

View File

@@ -10319,7 +10319,7 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -10391,7 +10391,7 @@ spec:
- command:
- /shared/argocd-dex
- rundex
image: ghcr.io/dexidp/dex:v2.35.3
image: ghcr.io/dexidp/dex:v2.36.0
imagePullPolicy: Always
name: dex
ports:
@@ -10416,7 +10416,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -10456,7 +10456,7 @@ spec:
containers:
- command:
- argocd-notifications
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -10525,7 +10525,7 @@ spec:
app.kubernetes.io/name: argocd-redis-ha-haproxy
topologyKey: kubernetes.io/hostname
containers:
- image: haproxy:2.0.29-alpine
- image: haproxy:2.0.31-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -10554,7 +10554,7 @@ spec:
- /readonly/haproxy_init.sh
command:
- sh
image: haproxy:2.0.29-alpine
image: haproxy:2.0.31-alpine
imagePullPolicy: IfNotPresent
name: config-init
volumeMounts:
@@ -10713,7 +10713,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -10762,7 +10762,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -11009,7 +11009,7 @@ spec:
key: otlp.address
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -11217,7 +11217,7 @@ spec:
key: otlp.address
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -11298,7 +11298,7 @@ spec:
- /data/conf/redis.conf
command:
- redis-server
image: redis:7.0.4-alpine
image: redis:7.0.8-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -11336,7 +11336,7 @@ spec:
- /data/conf/sentinel.conf
command:
- redis-sentinel
image: redis:7.0.4-alpine
image: redis:7.0.8-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -11382,7 +11382,7 @@ spec:
value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4
- name: SENTINEL_ID_2
value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca
image: redis:7.0.4-alpine
image: redis:7.0.8-alpine
imagePullPolicy: IfNotPresent
name: config-init
volumeMounts:

View File

@@ -1244,7 +1244,7 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -1316,7 +1316,7 @@ spec:
- command:
- /shared/argocd-dex
- rundex
image: ghcr.io/dexidp/dex:v2.35.3
image: ghcr.io/dexidp/dex:v2.36.0
imagePullPolicy: Always
name: dex
ports:
@@ -1341,7 +1341,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -1381,7 +1381,7 @@ spec:
containers:
- command:
- argocd-notifications
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -1450,7 +1450,7 @@ spec:
app.kubernetes.io/name: argocd-redis-ha-haproxy
topologyKey: kubernetes.io/hostname
containers:
- image: haproxy:2.0.29-alpine
- image: haproxy:2.0.31-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -1479,7 +1479,7 @@ spec:
- /readonly/haproxy_init.sh
command:
- sh
image: haproxy:2.0.29-alpine
image: haproxy:2.0.31-alpine
imagePullPolicy: IfNotPresent
name: config-init
volumeMounts:
@@ -1638,7 +1638,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -1687,7 +1687,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -1934,7 +1934,7 @@ spec:
key: otlp.address
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2142,7 +2142,7 @@ spec:
key: otlp.address
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2223,7 +2223,7 @@ spec:
- /data/conf/redis.conf
command:
- redis-server
image: redis:7.0.4-alpine
image: redis:7.0.8-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -2261,7 +2261,7 @@ spec:
- /data/conf/sentinel.conf
command:
- redis-sentinel
image: redis:7.0.4-alpine
image: redis:7.0.8-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -2307,7 +2307,7 @@ spec:
value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4
- name: SENTINEL_ID_2
value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca
image: redis:7.0.4-alpine
image: redis:7.0.8-alpine
imagePullPolicy: IfNotPresent
name: config-init
volumeMounts:

View File

@@ -9691,7 +9691,7 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -9763,7 +9763,7 @@ spec:
- command:
- /shared/argocd-dex
- rundex
image: ghcr.io/dexidp/dex:v2.35.3
image: ghcr.io/dexidp/dex:v2.36.0
imagePullPolicy: Always
name: dex
ports:
@@ -9788,7 +9788,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -9828,7 +9828,7 @@ spec:
containers:
- command:
- argocd-notifications
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -9903,7 +9903,7 @@ spec:
- ""
- --appendonly
- "no"
image: redis:7.0.4-alpine
image: redis:7.0.8-alpine
imagePullPolicy: Always
name: redis
ports:
@@ -10053,7 +10053,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -10102,7 +10102,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -10345,7 +10345,7 @@ spec:
key: otlp.address
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -10547,7 +10547,7 @@ spec:
key: otlp.address
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -616,7 +616,7 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -688,7 +688,7 @@ spec:
- command:
- /shared/argocd-dex
- rundex
image: ghcr.io/dexidp/dex:v2.35.3
image: ghcr.io/dexidp/dex:v2.36.0
imagePullPolicy: Always
name: dex
ports:
@@ -713,7 +713,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -753,7 +753,7 @@ spec:
containers:
- command:
- argocd-notifications
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -828,7 +828,7 @@ spec:
- ""
- --appendonly
- "no"
image: redis:7.0.4-alpine
image: redis:7.0.8-alpine
imagePullPolicy: Always
name: redis
ports:
@@ -978,7 +978,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -1027,7 +1027,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -1270,7 +1270,7 @@ spec:
key: otlp.address
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -1472,7 +1472,7 @@ spec:
key: otlp.address
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.4.18
image: quay.io/argoproj/argocd:v2.4.28
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -49,7 +49,9 @@ type ApplicationQuery struct {
// the selector to restrict returned list to applications only with matched labels
Selector *string `protobuf:"bytes,5,opt,name=selector" json:"selector,omitempty"`
// the repoURL to restrict returned list applications
Repo *string `protobuf:"bytes,6,opt,name=repo" json:"repo,omitempty"`
Repo *string `protobuf:"bytes,6,opt,name=repo" json:"repo,omitempty"`
// the project names to restrict returned list applications (legacy name for backwards-compatibility)
Project []string `protobuf:"bytes,8,rep,name=project" json:"project,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@@ -130,6 +132,13 @@ func (m *ApplicationQuery) GetRepo() string {
return ""
}
func (m *ApplicationQuery) GetProject() []string {
if m != nil {
return m.Project
}
return nil
}
type NodeQuery struct {
// the application's name
Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
@@ -2100,147 +2109,148 @@ func init() {
}
var fileDescriptor_df6e82b174b5eaec = []byte{
// 2236 bytes of a gzipped FileDescriptorProto
// 2243 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0xcd, 0x8f, 0x1b, 0x49,
0x15, 0x57, 0xcd, 0xa7, 0xfd, 0x9c, 0xcf, 0xda, 0x4d, 0xe8, 0xed, 0x4c, 0x66, 0x47, 0x95, 0xaf,
0xc9, 0x24, 0x63, 0x27, 0x26, 0x42, 0xd9, 0x59, 0x10, 0x64, 0x77, 0xc3, 0x6c, 0x96, 0x99, 0xd9,
0xd0, 0x93, 0x10, 0xb4, 0x1c, 0xa0, 0xb6, 0xbb, 0xec, 0x69, 0xc6, 0xee, 0xea, 0x74, 0xb7, 0x1d,
0x59, 0x21, 0x97, 0x45, 0xdc, 0x10, 0x48, 0xb0, 0x07, 0x84, 0x10, 0x42, 0xac, 0x56, 0xe2, 0x06,
0x5c, 0x56, 0x48, 0x5c, 0xe0, 0xc2, 0x87, 0xc4, 0x01, 0xc1, 0x3f, 0x00, 0x11, 0x27, 0x2e, 0x5c,
0x39, 0xa2, 0xaa, 0xae, 0x6a, 0x57, 0x7b, 0xec, 0xb6, 0xc3, 0x78, 0xb5, 0xb9, 0xf5, 0x2b, 0x57,
0xbd, 0xf7, 0xab, 0x57, 0xbf, 0x7a, 0xaf, 0xde, 0x93, 0xe1, 0x7c, 0xcc, 0xa2, 0x2e, 0x8b, 0x6a,
0x34, 0x0c, 0x5b, 0xbe, 0x4b, 0x13, 0x9f, 0x07, 0xe6, 0x77, 0x35, 0x8c, 0x78, 0xc2, 0x71, 0xc5,
0x18, 0xb2, 0x97, 0x9a, 0x9c, 0x37, 0x5b, 0xac, 0x46, 0x43, 0xbf, 0x46, 0x83, 0x80, 0x27, 0x72,
0x38, 0x4e, 0xa7, 0xda, 0x64, 0xff, 0x66, 0x5c, 0xf5, 0xb9, 0xfc, 0xd5, 0xe5, 0x11, 0xab, 0x75,
0xaf, 0xd7, 0x9a, 0x2c, 0x60, 0x11, 0x4d, 0x98, 0xa7, 0xe6, 0xdc, 0xe8, 0xcf, 0x69, 0x53, 0x77,
0xcf, 0x0f, 0x58, 0xd4, 0xab, 0x85, 0xfb, 0x4d, 0x31, 0x10, 0xd7, 0xda, 0x2c, 0xa1, 0xc3, 0x56,
0x6d, 0x35, 0xfd, 0x64, 0xaf, 0xf3, 0x6e, 0xd5, 0xe5, 0xed, 0x1a, 0x8d, 0x9a, 0x3c, 0x8c, 0xf8,
0x37, 0xe5, 0xc7, 0xba, 0xeb, 0xd5, 0xba, 0xf5, 0xbe, 0x02, 0x73, 0x2f, 0xdd, 0xeb, 0xb4, 0x15,
0xee, 0xd1, 0x83, 0xda, 0x6e, 0x8f, 0xd1, 0x16, 0xb1, 0x90, 0x2b, 0xdf, 0xc8, 0x4f, 0x3f, 0xe1,
0x51, 0xcf, 0xf8, 0x4c, 0xd5, 0x90, 0x8f, 0x10, 0x9c, 0xb8, 0xd5, 0xb7, 0xf7, 0xe5, 0x0e, 0x8b,
0x7a, 0x18, 0xc3, 0x5c, 0x40, 0xdb, 0xcc, 0x42, 0x2b, 0x68, 0xb5, 0xec, 0xc8, 0x6f, 0x6c, 0xc1,
0x62, 0xc4, 0x1a, 0x11, 0x8b, 0xf7, 0xac, 0x19, 0x39, 0xac, 0x45, 0x6c, 0x43, 0x49, 0x18, 0x67,
0x6e, 0x12, 0x5b, 0xb3, 0x2b, 0xb3, 0xab, 0x65, 0x27, 0x93, 0xf1, 0x2a, 0x1c, 0x8f, 0x58, 0xcc,
0x3b, 0x91, 0xcb, 0xbe, 0xc2, 0xa2, 0xd8, 0xe7, 0x81, 0x35, 0x27, 0x57, 0x0f, 0x0e, 0x0b, 0x2d,
0x31, 0x6b, 0x31, 0x37, 0xe1, 0x91, 0x35, 0x2f, 0xa7, 0x64, 0xb2, 0xc0, 0x23, 0x80, 0x5b, 0x0b,
0x29, 0x1e, 0xf1, 0x4d, 0x5e, 0x86, 0xf2, 0x0e, 0xf7, 0xd8, 0x48, 0xc0, 0x64, 0x13, 0x4e, 0x39,
0xac, 0xeb, 0x0b, 0xe5, 0xdb, 0x2c, 0xa1, 0x1e, 0x4d, 0xe8, 0xe0, 0xe4, 0x99, 0x6c, 0x77, 0x36,
0x94, 0x22, 0x35, 0xd9, 0x9a, 0x91, 0xe3, 0x99, 0x4c, 0x7e, 0x81, 0x60, 0xd9, 0x70, 0x91, 0xa3,
0x80, 0xdf, 0xee, 0xb2, 0x20, 0x89, 0x47, 0xab, 0xbc, 0x0a, 0x27, 0xf5, 0x1e, 0x77, 0x68, 0x9b,
0xc5, 0x21, 0x75, 0x99, 0x72, 0xdd, 0xc1, 0x1f, 0x30, 0x81, 0x23, 0xe6, 0xa0, 0x35, 0x2b, 0x27,
0xe6, 0xc6, 0xf0, 0x0a, 0x54, 0xb4, 0x7c, 0xff, 0xce, 0x1b, 0xca, 0x91, 0xe6, 0x10, 0x79, 0x0b,
0x2c, 0x03, 0xe9, 0x36, 0x0d, 0xfc, 0x06, 0x8b, 0x93, 0x49, 0xb7, 0x8d, 0x72, 0xdb, 0x3e, 0x05,
0x2f, 0xe4, 0x77, 0x1d, 0xf2, 0x20, 0x66, 0xe4, 0xb7, 0x28, 0x67, 0xe3, 0xf5, 0x88, 0xd1, 0x84,
0x39, 0xec, 0x61, 0x87, 0xc5, 0x09, 0xde, 0x07, 0xf3, 0xa6, 0x49, 0x53, 0x95, 0xfa, 0x9d, 0x6a,
0x9f, 0xaa, 0x55, 0x4d, 0x55, 0xf9, 0xf1, 0x75, 0xd7, 0xab, 0x76, 0xeb, 0xd5, 0x70, 0xbf, 0x59,
0x15, 0xc4, 0xaf, 0x9a, 0x17, 0x57, 0x13, 0xbf, 0x6a, 0x82, 0x30, 0xb5, 0xe3, 0xd3, 0xb0, 0xd0,
0x09, 0x63, 0x16, 0x25, 0x12, 0x7a, 0xc9, 0x51, 0x92, 0xd8, 0x54, 0x97, 0xb6, 0x7c, 0x8f, 0x26,
0xa9, 0x1b, 0x4b, 0x4e, 0x26, 0x93, 0x0f, 0xf2, 0xe8, 0xef, 0x87, 0xde, 0x27, 0x85, 0xde, 0x44,
0x39, 0x33, 0x80, 0xb2, 0x9b, 0x03, 0xf9, 0x06, 0x6b, 0xb1, 0x3e, 0xc8, 0x61, 0xc7, 0x68, 0xc1,
0xa2, 0x4b, 0x63, 0x97, 0x7a, 0x5a, 0x95, 0x16, 0x05, 0x09, 0xc3, 0x88, 0x87, 0xb4, 0x29, 0x35,
0xdd, 0xe5, 0x2d, 0xdf, 0xed, 0x29, 0x6e, 0x1d, 0xfc, 0x81, 0x9c, 0x83, 0xca, 0x6e, 0x2f, 0x70,
0xdf, 0x0e, 0x65, 0x40, 0xc4, 0x2f, 0xc2, 0xbc, 0x9f, 0xb0, 0x76, 0x6c, 0x21, 0x79, 0xab, 0x53,
0x81, 0xfc, 0x77, 0x0e, 0x4e, 0x1b, 0xe8, 0xc4, 0x82, 0x22, 0x6c, 0x05, 0x14, 0x13, 0x27, 0xe8,
0x45, 0x3d, 0xa7, 0x13, 0xa8, 0x73, 0x52, 0x92, 0x30, 0x1c, 0x46, 0x9d, 0x80, 0x49, 0x8a, 0x97,
0x9c, 0x54, 0xc0, 0x0d, 0x28, 0xc5, 0x89, 0x08, 0x81, 0xcd, 0x9e, 0x8c, 0x10, 0x95, 0xfa, 0x5b,
0x87, 0x3b, 0x1b, 0x01, 0x7d, 0x57, 0x69, 0x74, 0x32, 0xdd, 0xf8, 0x21, 0x94, 0xf5, 0x9d, 0x8a,
0xad, 0xc5, 0x95, 0xd9, 0xd5, 0x4a, 0x7d, 0xf7, 0xf0, 0x86, 0xde, 0x0e, 0x45, 0xf8, 0x36, 0xe2,
0x87, 0xd3, 0xb7, 0x82, 0x97, 0xa0, 0xdc, 0x56, 0x97, 0x35, 0xb6, 0x4a, 0xd2, 0xdb, 0xfd, 0x01,
0xfc, 0x55, 0x98, 0xf7, 0x83, 0x06, 0x8f, 0xad, 0xb2, 0x04, 0xf3, 0xda, 0xe1, 0xc0, 0xdc, 0x09,
0x1a, 0xdc, 0x49, 0x15, 0xe2, 0x87, 0x70, 0x34, 0x62, 0x49, 0xd4, 0xd3, 0x5e, 0xb0, 0x40, 0xfa,
0xf5, 0x4b, 0x87, 0xb3, 0xe0, 0x98, 0x2a, 0x9d, 0xbc, 0x05, 0xbc, 0x01, 0x95, 0xb8, 0xcf, 0x31,
0xab, 0x22, 0x0d, 0x5a, 0x39, 0x45, 0x06, 0x07, 0x1d, 0x73, 0x32, 0xf9, 0x35, 0x82, 0xa5, 0x03,
0xb7, 0x77, 0x37, 0x64, 0x85, 0x04, 0xa4, 0x30, 0x17, 0x87, 0xcc, 0x95, 0x61, 0xbd, 0x52, 0xdf,
0x9e, 0xda, 0x75, 0x96, 0x76, 0xa5, 0xea, 0xc2, 0x88, 0x43, 0xe1, 0x53, 0xc6, 0xa2, 0xbb, 0x34,
0x71, 0xf7, 0x8a, 0xd0, 0x0a, 0xea, 0x8b, 0x39, 0x2a, 0x0b, 0xa5, 0x82, 0xe0, 0x87, 0xfc, 0xb8,
0xd7, 0x0b, 0x85, 0x05, 0xf1, 0x4b, 0x7f, 0x80, 0x04, 0x60, 0x9b, 0x61, 0x86, 0xb7, 0x5a, 0xef,
0x52, 0x77, 0xbf, 0xc8, 0xca, 0x31, 0x98, 0xf1, 0x3d, 0x69, 0x62, 0xd6, 0x99, 0xf1, 0xbd, 0x67,
0xbb, 0x88, 0xe2, 0xcd, 0x60, 0x0f, 0x49, 0x88, 0x45, 0x06, 0x97, 0xa0, 0x1c, 0x0c, 0x24, 0xc1,
0xfe, 0xc0, 0x90, 0xe4, 0x37, 0x73, 0x20, 0xf9, 0x59, 0xb0, 0xd8, 0xcd, 0x5e, 0x10, 0xe2, 0x67,
0x2d, 0x0a, 0x90, 0xcd, 0x88, 0x77, 0x42, 0xf5, 0x6c, 0x48, 0x05, 0x81, 0x62, 0xdf, 0x0f, 0x3c,
0x6b, 0x21, 0x45, 0x21, 0xbe, 0xc9, 0x7f, 0x10, 0xbc, 0x3c, 0x04, 0xf8, 0xd8, 0x43, 0x79, 0x2e,
0xd0, 0xf7, 0xa9, 0xb1, 0x38, 0x92, 0x1a, 0xa5, 0x41, 0x6a, 0xfc, 0x1b, 0xc1, 0xca, 0x90, 0x1d,
0x8f, 0x4f, 0x29, 0xcf, 0xcd, 0x96, 0x1b, 0x3c, 0x72, 0x99, 0xb5, 0x98, 0xf2, 0x4f, 0x0a, 0x82,
0xad, 0x3c, 0x0a, 0xf7, 0x68, 0x60, 0x95, 0x52, 0xb6, 0xa6, 0x12, 0xf9, 0x0b, 0x02, 0x4b, 0xef,
0xf0, 0x96, 0x2b, 0xf7, 0xdb, 0x09, 0x9e, 0xff, 0x4d, 0x9e, 0x86, 0x05, 0x2a, 0xd1, 0xaa, 0x83,
0x55, 0x12, 0xf9, 0x0e, 0x82, 0x33, 0xf9, 0xed, 0xc4, 0x5b, 0x7e, 0x9c, 0xe8, 0x97, 0x18, 0x6e,
0xc0, 0x62, 0x3a, 0x33, 0x4d, 0xd0, 0x95, 0xfa, 0xd6, 0x61, 0xc3, 0x76, 0xce, 0x75, 0x5a, 0x39,
0x79, 0x05, 0xce, 0x0c, 0xbd, 0xed, 0x0a, 0x86, 0x0d, 0x25, 0x9d, 0xaa, 0x94, 0x73, 0x33, 0x99,
0xfc, 0x71, 0x36, 0x1f, 0xfd, 0xb8, 0xb7, 0xc5, 0x9b, 0x05, 0x6f, 0xe6, 0xe2, 0x03, 0xb1, 0x60,
0x31, 0xe4, 0x9e, 0xf1, 0x3c, 0xd6, 0xa2, 0x58, 0xe7, 0xf2, 0x20, 0xa1, 0xa2, 0x12, 0x53, 0xef,
0xe2, 0xfe, 0x80, 0x38, 0xc8, 0xd8, 0x0f, 0x5c, 0xb6, 0xcb, 0x5c, 0x1e, 0x78, 0xb1, 0x3c, 0x91,
0x59, 0x27, 0x37, 0x86, 0xdf, 0x84, 0xb2, 0x94, 0xef, 0xf9, 0x6d, 0x26, 0xeb, 0x8c, 0x4a, 0x7d,
0xad, 0x9a, 0x96, 0x79, 0x55, 0xb3, 0xcc, 0xeb, 0xfb, 0x50, 0x94, 0x79, 0xd5, 0xee, 0xf5, 0xaa,
0x58, 0xe1, 0xf4, 0x17, 0x0b, 0x2c, 0x09, 0xf5, 0x5b, 0x5b, 0x7e, 0x20, 0x9f, 0x0f, 0xc2, 0x54,
0x7f, 0x40, 0x1c, 0x76, 0x83, 0xb7, 0x5a, 0xfc, 0x91, 0xe6, 0x6e, 0x2a, 0x89, 0x55, 0x9d, 0x20,
0xf1, 0x5b, 0xd2, 0x7e, 0x39, 0xdd, 0x41, 0x36, 0x20, 0x57, 0xf9, 0xad, 0x84, 0x45, 0x32, 0x41,
0x97, 0x1d, 0x25, 0x65, 0x74, 0xaa, 0xa4, 0x75, 0x8f, 0xbe, 0x33, 0x29, 0xf1, 0x8e, 0x98, 0xc4,
0x1b, 0x24, 0xf3, 0xd1, 0x21, 0xf5, 0x85, 0x2c, 0xe4, 0x58, 0xd7, 0xe7, 0x9d, 0xd8, 0x3a, 0x96,
0xa6, 0x31, 0x2d, 0x93, 0xdf, 0x21, 0x28, 0x6d, 0xf1, 0xe6, 0xed, 0x20, 0x89, 0x7a, 0xf2, 0xbd,
0xc9, 0x83, 0x84, 0x05, 0xfa, 0xc4, 0xb5, 0x28, 0xdc, 0x98, 0xf8, 0x6d, 0xb6, 0x9b, 0xd0, 0x76,
0xa8, 0x32, 0xee, 0x33, 0xb9, 0x31, 0x5b, 0x2c, 0xb6, 0xd6, 0xa2, 0x71, 0x22, 0x6f, 0x5d, 0xc9,
0x91, 0xdf, 0x62, 0x13, 0xd9, 0x84, 0xdd, 0x24, 0x52, 0x57, 0x2e, 0x37, 0x66, 0x92, 0x64, 0x3e,
0xc5, 0xa6, 0x44, 0x52, 0x83, 0x97, 0xb2, 0x47, 0xd8, 0x3d, 0x16, 0xb5, 0xfd, 0x80, 0x16, 0xc6,
0x40, 0x72, 0x3d, 0x47, 0x7c, 0xf1, 0x2a, 0x79, 0xe0, 0x07, 0x1e, 0x7f, 0x34, 0x9a, 0xc0, 0xe4,
0x6f, 0xf9, 0x5a, 0xd1, 0x58, 0x93, 0xdd, 0x97, 0x37, 0xe1, 0xa8, 0xb8, 0x59, 0x5d, 0xa6, 0x7e,
0x50, 0x97, 0x97, 0xe4, 0x2e, 0xe5, 0x50, 0x1d, 0x4e, 0x7e, 0x21, 0xde, 0x82, 0xe3, 0x34, 0x8e,
0xfd, 0x66, 0xc0, 0x3c, 0xad, 0x6b, 0x66, 0x62, 0x5d, 0x83, 0x4b, 0xd3, 0x22, 0x42, 0xce, 0x50,
0x3e, 0xd7, 0x22, 0xf9, 0x36, 0x82, 0x53, 0x43, 0x95, 0x64, 0xfc, 0x43, 0x46, 0x38, 0x13, 0x85,
0xbc, 0xbb, 0xc7, 0xbc, 0x4e, 0x8b, 0xe9, 0x52, 0x5a, 0xcb, 0xe2, 0x37, 0xaf, 0x93, 0x9e, 0x80,
0x0a, 0xa7, 0x99, 0x8c, 0x97, 0x01, 0xda, 0x34, 0xe8, 0xd0, 0x96, 0x84, 0x30, 0x27, 0x21, 0x18,
0x23, 0x64, 0x09, 0xec, 0x61, 0xc7, 0xa7, 0xca, 0xd2, 0x5f, 0x21, 0x38, 0xa6, 0x43, 0x93, 0x3a,
0x9f, 0x55, 0x38, 0x6e, 0xb8, 0x61, 0xa7, 0x7f, 0x54, 0x83, 0xc3, 0x63, 0xc2, 0x8e, 0x3e, 0xe7,
0xd9, 0x7c, 0x37, 0xa4, 0x9b, 0xeb, 0x67, 0x4c, 0x1c, 0xf7, 0xb3, 0x8b, 0x4a, 0xbe, 0x05, 0xd6,
0x36, 0x0d, 0x68, 0x93, 0x79, 0x19, 0xf0, 0x8c, 0x24, 0xdf, 0x30, 0x4b, 0xaf, 0x43, 0x17, 0x3a,
0x59, 0xda, 0xf7, 0x1b, 0x0d, 0x55, 0xc6, 0xd5, 0xff, 0xb9, 0x0c, 0xd8, 0x3c, 0x54, 0x16, 0x75,
0x7d, 0x97, 0xe1, 0x1f, 0x20, 0x98, 0x13, 0x59, 0x06, 0x9f, 0x1d, 0xc5, 0x21, 0xe9, 0x5c, 0x7b,
0x7a, 0xef, 0x68, 0x61, 0x8d, 0x2c, 0xbd, 0xf7, 0xf7, 0x7f, 0xfd, 0x70, 0xe6, 0x34, 0x7e, 0x51,
0xf6, 0xdd, 0xba, 0xd7, 0xcd, 0x1e, 0x58, 0x8c, 0xbf, 0x8b, 0x00, 0xab, 0xd4, 0x67, 0xb4, 0x5e,
0xf0, 0x95, 0x51, 0x10, 0x87, 0xb4, 0x68, 0xec, 0xb3, 0x46, 0x18, 0xaa, 0xba, 0x3c, 0x62, 0x22,
0xe8, 0xc8, 0x09, 0x12, 0xc0, 0x9a, 0x04, 0x70, 0x1e, 0x93, 0x61, 0x00, 0x6a, 0x8f, 0xc5, 0xa1,
0x3f, 0xa9, 0xb1, 0xd4, 0xee, 0xcf, 0x11, 0xcc, 0x3f, 0x90, 0x8f, 0xaf, 0x31, 0x4e, 0xda, 0x9d,
0x9a, 0x93, 0xa4, 0x39, 0x89, 0x96, 0x9c, 0x93, 0x48, 0xcf, 0xe2, 0x33, 0x1a, 0x69, 0x9c, 0x44,
0x8c, 0xb6, 0x73, 0x80, 0xaf, 0x21, 0xfc, 0x21, 0x82, 0x85, 0xb4, 0x39, 0x83, 0x2f, 0x8c, 0x42,
0x99, 0x6b, 0xde, 0xd8, 0xd3, 0xeb, 0x74, 0x90, 0xcb, 0x12, 0xe3, 0x39, 0x32, 0xf4, 0x38, 0x37,
0x72, 0x7d, 0x90, 0xf7, 0x11, 0xcc, 0x6e, 0xb2, 0xb1, 0x7c, 0x9b, 0x22, 0xb8, 0x03, 0x0e, 0x1c,
0x72, 0xd4, 0xf8, 0x03, 0x04, 0x2f, 0x6d, 0xb2, 0x64, 0x78, 0x2c, 0xc7, 0xab, 0xe3, 0x03, 0xac,
0xa2, 0xdd, 0x95, 0x09, 0x66, 0x66, 0x41, 0xac, 0x26, 0x91, 0x5d, 0xc6, 0x97, 0x8a, 0x48, 0x28,
0x0a, 0xe2, 0x47, 0x0a, 0xc7, 0x9f, 0x11, 0x9c, 0x18, 0x6c, 0x72, 0xe2, 0x7c, 0xf4, 0x1f, 0xda,
0x03, 0xb5, 0x77, 0x0e, 0x1b, 0x50, 0xf2, 0x4a, 0xc9, 0x2d, 0x89, 0xfc, 0x55, 0xfc, 0x4a, 0x11,
0x72, 0xdd, 0xf7, 0x89, 0x6b, 0x8f, 0xf5, 0xe7, 0x13, 0xd9, 0x2d, 0x97, 0xb0, 0xdf, 0x43, 0x70,
0x64, 0x93, 0x25, 0xdb, 0x59, 0xdb, 0x63, 0x24, 0x6d, 0x73, 0x7d, 0x4d, 0x7b, 0xa9, 0x6a, 0x34,
0xb5, 0xf5, 0x4f, 0x99, 0x4b, 0xd7, 0x25, 0xb0, 0x4b, 0xf8, 0x42, 0x11, 0xb0, 0x7e, 0xab, 0xe5,
0xf7, 0x08, 0x16, 0xd2, 0xb6, 0xc2, 0x68, 0xf3, 0xb9, 0xa6, 0xe1, 0x34, 0x89, 0x79, 0x5b, 0x62,
0xfd, 0xbc, 0x7d, 0x6d, 0x38, 0x56, 0x73, 0xbd, 0xf6, 0x5a, 0x55, 0x6e, 0x20, 0x7f, 0xa3, 0x3e,
0x42, 0x00, 0xfd, 0xd6, 0x08, 0xbe, 0x5c, 0xbc, 0x0f, 0xa3, 0x7d, 0x62, 0x4f, 0xb7, 0x39, 0x42,
0xaa, 0x72, 0x3f, 0xab, 0xf6, 0x4a, 0x21, 0x9d, 0x43, 0xe6, 0x6e, 0xa4, 0x6d, 0x94, 0x9f, 0x21,
0x98, 0x97, 0xb5, 0x38, 0x3e, 0x3f, 0x0a, 0xb3, 0x59, 0xaa, 0x4f, 0xd3, 0xf5, 0x17, 0x25, 0xd4,
0x95, 0x7a, 0x51, 0x4c, 0xd8, 0x40, 0x6b, 0xb8, 0x0b, 0x0b, 0x69, 0xed, 0x3c, 0x9a, 0x1e, 0xb9,
0xda, 0xda, 0x5e, 0x29, 0xc8, 0x51, 0x29, 0x43, 0x55, 0x38, 0x5a, 0x1b, 0x17, 0x8e, 0xe6, 0x44,
0xc4, 0xc0, 0xe7, 0x8a, 0xe2, 0xc9, 0xc7, 0xe0, 0x98, 0x2b, 0x12, 0xdd, 0x05, 0xb2, 0x32, 0x2e,
0x24, 0x09, 0xef, 0xfc, 0x08, 0xc1, 0x89, 0xc1, 0x27, 0x0d, 0x3e, 0x33, 0x10, 0x8e, 0xcc, 0x37,
0x9a, 0x9d, 0xf7, 0xe2, 0xa8, 0xe7, 0x10, 0xf9, 0x82, 0x44, 0xb1, 0x81, 0x6f, 0x8e, 0xbd, 0x19,
0x3b, 0xfa, 0x42, 0x0b, 0x45, 0xeb, 0xfd, 0x0e, 0xeb, 0x6f, 0x10, 0x1c, 0xd1, 0x7a, 0xef, 0x45,
0x8c, 0x15, 0xc3, 0x9a, 0xde, 0x45, 0x10, 0xb6, 0xc8, 0x67, 0x25, 0xfc, 0xcf, 0xe0, 0x1b, 0x13,
0xc2, 0xd7, 0xb0, 0xd7, 0x13, 0x81, 0xf4, 0x0f, 0x08, 0x4e, 0x3e, 0x48, 0x79, 0xff, 0x09, 0xe1,
0x7f, 0x5d, 0xe2, 0xff, 0x1c, 0x7e, 0xb5, 0xe0, 0xc9, 0x31, 0x6e, 0x1b, 0xd7, 0x10, 0xfe, 0x25,
0x82, 0x92, 0xee, 0x4e, 0xe2, 0x4b, 0x23, 0x2f, 0x46, 0xbe, 0x7f, 0x39, 0x4d, 0x32, 0xab, 0xfc,
0x4a, 0xce, 0x17, 0x66, 0x29, 0x65, 0x5f, 0x10, 0xfa, 0x7d, 0x04, 0x38, 0xab, 0x35, 0xb2, 0xea,
0x03, 0x5f, 0xcc, 0x99, 0x1a, 0x59, 0x54, 0xda, 0x97, 0xc6, 0xce, 0xcb, 0x67, 0xa9, 0xb5, 0xc2,
0x2c, 0xc5, 0x33, 0xfb, 0xdf, 0x43, 0x50, 0xd9, 0x64, 0xd9, 0x73, 0xb8, 0xc0, 0x97, 0xf9, 0xd6,
0xac, 0xbd, 0x3a, 0x7e, 0xa2, 0x42, 0x74, 0x55, 0x22, 0xba, 0x88, 0x8b, 0x5d, 0xa5, 0x01, 0xfc,
0x04, 0xc1, 0xd1, 0xbb, 0x26, 0x45, 0xf1, 0xd5, 0x71, 0x96, 0x72, 0x91, 0x7c, 0x72, 0x5c, 0x9f,
0x96, 0xb8, 0xd6, 0xc9, 0x44, 0xb8, 0x36, 0x54, 0x8f, 0xf4, 0xa7, 0x08, 0x5e, 0x30, 0xeb, 0x07,
0xd5, 0x4d, 0xfb, 0x7f, 0xfd, 0x56, 0xd0, 0x94, 0x23, 0x37, 0x24, 0xbe, 0x2a, 0xbe, 0x3a, 0x09,
0xbe, 0x9a, 0x6a, 0xb1, 0xe1, 0x1f, 0x23, 0x38, 0x29, 0x7b, 0x95, 0xa6, 0xe2, 0x81, 0x14, 0x33,
0xaa, 0xb3, 0x39, 0x41, 0x8a, 0x51, 0xf1, 0x87, 0x3c, 0x13, 0xa8, 0x0d, 0xd5, 0x87, 0xc4, 0xdf,
0x47, 0x70, 0x4c, 0x27, 0x35, 0x75, 0xba, 0xeb, 0xe3, 0x1c, 0xf7, 0xac, 0x49, 0x50, 0xd1, 0x6d,
0x6d, 0x32, 0xba, 0x7d, 0x88, 0x60, 0x51, 0xf5, 0x12, 0x0b, 0x9e, 0x0a, 0x46, 0xb3, 0xd1, 0x3e,
0x95, 0x9b, 0xa5, 0x1b, 0x59, 0xe4, 0x6b, 0xd2, 0xec, 0x7d, 0x5c, 0x2b, 0x32, 0x1b, 0x72, 0x2f,
0xae, 0x3d, 0x56, 0x5d, 0xa4, 0x27, 0xb5, 0x16, 0x6f, 0xc6, 0xef, 0x10, 0x5c, 0x98, 0x10, 0xc5,
0x9c, 0x6b, 0xe8, 0xb5, 0x2f, 0xfe, 0xe9, 0xe9, 0x32, 0xfa, 0xeb, 0xd3, 0x65, 0xf4, 0x8f, 0xa7,
0xcb, 0xe8, 0x9d, 0x9b, 0x93, 0xfd, 0xff, 0xc3, 0x6d, 0xf9, 0x2c, 0x48, 0x4c, 0xb5, 0xff, 0x0b,
0x00, 0x00, 0xff, 0xff, 0xa3, 0x87, 0x84, 0x43, 0xe5, 0x22, 0x00, 0x00,
0x5c, 0x56, 0x48, 0x5c, 0xe0, 0xc2, 0x87, 0xc4, 0x61, 0x05, 0xff, 0x00, 0x44, 0x9c, 0xb8, 0x70,
0xe5, 0x88, 0xaa, 0xba, 0xaa, 0x5d, 0xed, 0xb1, 0xdb, 0x0e, 0x63, 0xb4, 0xb9, 0xf5, 0x2b, 0x57,
0xbd, 0xf7, 0x7b, 0xaf, 0xde, 0x47, 0xbd, 0x27, 0xc3, 0xf9, 0x98, 0x45, 0x5d, 0x16, 0xd5, 0x68,
0x18, 0xb6, 0x7c, 0x97, 0x26, 0x3e, 0x0f, 0xcc, 0xef, 0x6a, 0x18, 0xf1, 0x84, 0xe3, 0x8a, 0xb1,
0x64, 0x2f, 0x35, 0x39, 0x6f, 0xb6, 0x58, 0x8d, 0x86, 0x7e, 0x8d, 0x06, 0x01, 0x4f, 0xe4, 0x72,
0x9c, 0x6e, 0xb5, 0xc9, 0xfe, 0xcd, 0xb8, 0xea, 0x73, 0xf9, 0xab, 0xcb, 0x23, 0x56, 0xeb, 0x5e,
0xaf, 0x35, 0x59, 0xc0, 0x22, 0x9a, 0x30, 0x4f, 0xed, 0xb9, 0xd1, 0xdf, 0xd3, 0xa6, 0xee, 0x9e,
0x1f, 0xb0, 0xa8, 0x57, 0x0b, 0xf7, 0x9b, 0x62, 0x21, 0xae, 0xb5, 0x59, 0x42, 0x87, 0x9d, 0xda,
0x6a, 0xfa, 0xc9, 0x5e, 0xe7, 0xdd, 0xaa, 0xcb, 0xdb, 0x35, 0x1a, 0x35, 0x79, 0x18, 0xf1, 0x6f,
0xca, 0x8f, 0x75, 0xd7, 0xab, 0x75, 0xeb, 0x7d, 0x06, 0xa6, 0x2e, 0xdd, 0xeb, 0xb4, 0x15, 0xee,
0xd1, 0x83, 0xdc, 0x6e, 0x8f, 0xe1, 0x16, 0xb1, 0x90, 0x2b, 0xdb, 0xc8, 0x4f, 0x3f, 0xe1, 0x51,
0xcf, 0xf8, 0x4c, 0xd9, 0x90, 0x8f, 0x11, 0x9c, 0xb8, 0xd5, 0x97, 0xf7, 0xe5, 0x0e, 0x8b, 0x7a,
0x18, 0xc3, 0x5c, 0x40, 0xdb, 0xcc, 0x42, 0x2b, 0x68, 0xb5, 0xec, 0xc8, 0x6f, 0x6c, 0xc1, 0x62,
0xc4, 0x1a, 0x11, 0x8b, 0xf7, 0xac, 0x19, 0xb9, 0xac, 0x49, 0x6c, 0x43, 0x49, 0x08, 0x67, 0x6e,
0x12, 0x5b, 0xb3, 0x2b, 0xb3, 0xab, 0x65, 0x27, 0xa3, 0xf1, 0x2a, 0x1c, 0x8f, 0x58, 0xcc, 0x3b,
0x91, 0xcb, 0xbe, 0xc2, 0xa2, 0xd8, 0xe7, 0x81, 0x35, 0x27, 0x4f, 0x0f, 0x2e, 0x0b, 0x2e, 0x31,
0x6b, 0x31, 0x37, 0xe1, 0x91, 0x35, 0x2f, 0xb7, 0x64, 0xb4, 0xc0, 0x23, 0x80, 0x5b, 0x0b, 0x29,
0x1e, 0xf1, 0x2d, 0xf0, 0x28, 0x29, 0x56, 0x49, 0x0a, 0xd5, 0x24, 0x79, 0x19, 0xca, 0x3b, 0xdc,
0x63, 0x23, 0x55, 0x21, 0x9b, 0x70, 0xca, 0x61, 0x5d, 0x5f, 0x88, 0xdd, 0x66, 0x09, 0xf5, 0x68,
0x42, 0x07, 0x37, 0xcf, 0x64, 0x7a, 0xdb, 0x50, 0x8a, 0xd4, 0x66, 0x6b, 0x46, 0xae, 0x67, 0x34,
0xf9, 0x05, 0x82, 0x65, 0xc3, 0x78, 0x8e, 0x52, 0xe9, 0x76, 0x97, 0x05, 0x49, 0x3c, 0x9a, 0xe5,
0x55, 0x38, 0xa9, 0xb5, 0xdf, 0xa1, 0x6d, 0x16, 0x87, 0xd4, 0x65, 0xca, 0xa8, 0x07, 0x7f, 0xc0,
0x04, 0x8e, 0x98, 0x8b, 0xd6, 0xac, 0xdc, 0x98, 0x5b, 0xc3, 0x2b, 0x50, 0xd1, 0xf4, 0xfd, 0x3b,
0x6f, 0x28, 0x13, 0x9b, 0x4b, 0xe4, 0x2d, 0xb0, 0x0c, 0xa4, 0xdb, 0x34, 0xf0, 0x1b, 0x2c, 0x4e,
0x26, 0x55, 0x1b, 0xe5, 0xd4, 0x3e, 0x05, 0x2f, 0xe4, 0xb5, 0x0e, 0x79, 0x10, 0x33, 0xf2, 0x5b,
0x94, 0x93, 0xf1, 0x7a, 0xc4, 0x68, 0xc2, 0x1c, 0xf6, 0xb0, 0xc3, 0xe2, 0x04, 0xef, 0x83, 0x19,
0x83, 0x52, 0x54, 0xa5, 0x7e, 0xa7, 0xda, 0x77, 0xe2, 0xaa, 0x76, 0x62, 0xf9, 0xf1, 0x75, 0xd7,
0xab, 0x76, 0xeb, 0xd5, 0x70, 0xbf, 0x59, 0x15, 0x21, 0x51, 0x35, 0x43, 0x5a, 0x87, 0x44, 0xd5,
0x04, 0x61, 0x72, 0xc7, 0xa7, 0x61, 0xa1, 0x13, 0xc6, 0x2c, 0x4a, 0x24, 0xf4, 0x92, 0xa3, 0x28,
0xa1, 0x54, 0x97, 0xb6, 0x7c, 0x8f, 0x26, 0xa9, 0x19, 0x4b, 0x4e, 0x46, 0x93, 0x0f, 0xf2, 0xe8,
0xef, 0x87, 0xde, 0x27, 0x85, 0xde, 0x44, 0x39, 0x33, 0x80, 0xb2, 0x9b, 0x03, 0xf9, 0x06, 0x6b,
0xb1, 0x3e, 0xc8, 0x61, 0xd7, 0x68, 0xc1, 0xa2, 0x4b, 0x63, 0x97, 0x7a, 0x9a, 0x95, 0x26, 0x85,
0x13, 0x86, 0x11, 0x0f, 0x69, 0x53, 0x72, 0xba, 0xcb, 0x5b, 0xbe, 0xdb, 0x53, 0xbe, 0x75, 0xf0,
0x07, 0x72, 0x0e, 0x2a, 0xbb, 0xbd, 0xc0, 0x7d, 0x3b, 0x94, 0xa9, 0x12, 0xbf, 0x08, 0xf3, 0x7e,
0xc2, 0xda, 0xb1, 0x85, 0x64, 0xe8, 0xa5, 0x04, 0xf9, 0xcf, 0x1c, 0x9c, 0x36, 0xd0, 0x89, 0x03,
0x45, 0xd8, 0x0a, 0x5c, 0x4c, 0xdc, 0xa0, 0x17, 0xf5, 0x9c, 0x4e, 0xa0, 0xee, 0x49, 0x51, 0x42,
0x70, 0x18, 0x75, 0x02, 0x26, 0x5d, 0xbc, 0xe4, 0xa4, 0x04, 0x6e, 0x40, 0x29, 0x4e, 0x44, 0x72,
0x6c, 0xf6, 0x64, 0xee, 0xa8, 0xd4, 0xdf, 0x3a, 0xdc, 0xdd, 0x08, 0xe8, 0xbb, 0x8a, 0xa3, 0x93,
0xf1, 0xc6, 0x0f, 0xa1, 0xac, 0x63, 0x2a, 0xb6, 0x16, 0x57, 0x66, 0x57, 0x2b, 0xf5, 0xdd, 0xc3,
0x0b, 0x7a, 0x3b, 0x14, 0x89, 0xdd, 0xc8, 0x1f, 0x4e, 0x5f, 0x0a, 0x5e, 0x82, 0x72, 0x5b, 0x05,
0x6b, 0xac, 0x12, 0x5d, 0x7f, 0x01, 0x7f, 0x15, 0xe6, 0xfd, 0xa0, 0xc1, 0x63, 0xab, 0x2c, 0xc1,
0xbc, 0x76, 0x38, 0x30, 0x77, 0x82, 0x06, 0x77, 0x52, 0x86, 0xf8, 0x21, 0x1c, 0x8d, 0x58, 0x12,
0xf5, 0xb4, 0x15, 0x2c, 0x90, 0x76, 0xfd, 0xd2, 0xe1, 0x24, 0x38, 0x26, 0x4b, 0x27, 0x2f, 0x01,
0x6f, 0x40, 0x25, 0xee, 0xfb, 0x98, 0x55, 0x91, 0x02, 0xad, 0x1c, 0x23, 0xc3, 0x07, 0x1d, 0x73,
0x33, 0xf9, 0x35, 0x82, 0xa5, 0x03, 0xd1, 0xbb, 0x1b, 0xb2, 0x42, 0x07, 0xa4, 0x30, 0x17, 0x87,
0xcc, 0x95, 0x69, 0xbd, 0x52, 0xdf, 0x9e, 0x5a, 0x38, 0x4b, 0xb9, 0x92, 0x75, 0x61, 0xc6, 0xa1,
0xf0, 0x29, 0xe3, 0xd0, 0x5d, 0x9a, 0xb8, 0x7b, 0x45, 0x68, 0x85, 0xeb, 0x8b, 0x3d, 0xaa, 0x0a,
0xa5, 0x84, 0xf0, 0x0f, 0xf9, 0x71, 0xaf, 0x17, 0x0a, 0x09, 0xe2, 0x97, 0xfe, 0x02, 0x09, 0xc0,
0x36, 0xd3, 0x0c, 0x6f, 0xb5, 0xde, 0xa5, 0xee, 0x7e, 0x91, 0x94, 0x63, 0x30, 0xe3, 0x7b, 0x52,
0xc4, 0xac, 0x33, 0xe3, 0x7b, 0xcf, 0x16, 0x88, 0xe4, 0x23, 0x94, 0x17, 0xa8, 0x1d, 0xba, 0x40,
0xe0, 0x12, 0x94, 0x83, 0x81, 0x22, 0xd8, 0x5f, 0x18, 0x52, 0xfc, 0x66, 0x0e, 0x14, 0x3f, 0x0b,
0x16, 0xbb, 0xd9, 0xdb, 0x42, 0xfc, 0xac, 0x49, 0x01, 0xb2, 0x19, 0xf1, 0x4e, 0xa8, 0x1e, 0x14,
0x29, 0x21, 0x50, 0xec, 0xfb, 0x81, 0x67, 0x2d, 0xa4, 0x28, 0xc4, 0x37, 0xf9, 0x37, 0x82, 0x97,
0x87, 0x00, 0x1f, 0x7b, 0x29, 0xcf, 0x05, 0xfa, 0xbe, 0x6b, 0x2c, 0x8e, 0x74, 0x8d, 0xd2, 0xa0,
0x6b, 0xfc, 0x0b, 0xc1, 0xca, 0x10, 0x8d, 0xc7, 0x97, 0x94, 0xe7, 0x46, 0xe5, 0x06, 0x8f, 0x5c,
0x66, 0x2d, 0xa6, 0xfe, 0x27, 0x09, 0xe1, 0xad, 0x3c, 0x0a, 0xf7, 0x68, 0x60, 0x95, 0x52, 0x6f,
0x4d, 0x29, 0xf2, 0x17, 0x04, 0x96, 0xd6, 0xf0, 0x96, 0x2b, 0xf5, 0xed, 0x04, 0xcf, 0xbf, 0x92,
0xa7, 0x61, 0x81, 0x4a, 0xb4, 0xea, 0x62, 0x15, 0x45, 0xbe, 0x83, 0xe0, 0x4c, 0x5e, 0x9d, 0x78,
0xcb, 0x8f, 0x13, 0xfd, 0x12, 0xc3, 0x0d, 0x58, 0x4c, 0x77, 0xa6, 0x05, 0xba, 0x52, 0xdf, 0x3a,
0x6c, 0xda, 0xce, 0x99, 0x4e, 0x33, 0x27, 0xaf, 0xc0, 0x99, 0xa1, 0xd1, 0xae, 0x60, 0xd8, 0x50,
0xd2, 0xa5, 0x4a, 0x19, 0x37, 0xa3, 0xc9, 0x1f, 0x67, 0xf3, 0xd9, 0x8f, 0x7b, 0x5b, 0xbc, 0x59,
0xf0, 0x66, 0x2e, 0xbe, 0x10, 0xd1, 0x0c, 0x70, 0xcf, 0x78, 0x1e, 0x6b, 0x52, 0x9c, 0x73, 0x79,
0x90, 0x50, 0xd1, 0xa3, 0xa9, 0x77, 0x71, 0x7f, 0x41, 0x5c, 0x64, 0xec, 0x07, 0x2e, 0xdb, 0x65,
0x2e, 0x0f, 0xbc, 0x58, 0xde, 0xc8, 0xac, 0x93, 0x5b, 0xc3, 0x6f, 0x42, 0x59, 0xd2, 0xf7, 0xfc,
0x36, 0x93, 0x1d, 0x48, 0xa5, 0xbe, 0x56, 0x4d, 0x1b, 0xc0, 0xaa, 0xd9, 0x00, 0xf6, 0x6d, 0x28,
0x1a, 0xc0, 0x6a, 0xf7, 0x7a, 0x55, 0x9c, 0x70, 0xfa, 0x87, 0x05, 0x96, 0x84, 0xfa, 0xad, 0x2d,
0x3f, 0x90, 0xcf, 0x07, 0x21, 0xaa, 0xbf, 0x20, 0x2e, 0xbb, 0xc1, 0x5b, 0x2d, 0xfe, 0x48, 0xfb,
0x6e, 0x4a, 0x89, 0x53, 0x9d, 0x20, 0xf1, 0x5b, 0x52, 0x7e, 0x39, 0xd5, 0x20, 0x5b, 0x90, 0xa7,
0xfc, 0x56, 0xc2, 0x22, 0x59, 0xa0, 0xcb, 0x8e, 0xa2, 0x32, 0x77, 0xaa, 0xa4, 0x7d, 0x8f, 0x8e,
0x99, 0xd4, 0xf1, 0x8e, 0x98, 0x8e, 0x37, 0xe8, 0xcc, 0x47, 0x87, 0xf4, 0x17, 0xb2, 0xc5, 0x63,
0x5d, 0x9f, 0x77, 0x62, 0xeb, 0x58, 0x5a, 0xc6, 0x34, 0x4d, 0x7e, 0x87, 0xa0, 0xb4, 0xc5, 0x9b,
0xb7, 0x83, 0x24, 0xea, 0xc9, 0xf7, 0x26, 0x0f, 0x12, 0x16, 0xe8, 0x1b, 0xd7, 0xa4, 0x30, 0x63,
0xe2, 0xb7, 0xd9, 0x6e, 0x42, 0xdb, 0xa1, 0xaa, 0xb8, 0xcf, 0x64, 0xc6, 0xec, 0xb0, 0x50, 0xad,
0x45, 0xe3, 0x44, 0x46, 0x5d, 0xc9, 0x91, 0xdf, 0x42, 0x89, 0x6c, 0xc3, 0x6e, 0x12, 0xa9, 0x90,
0xcb, 0xad, 0x99, 0x4e, 0x32, 0x9f, 0x62, 0x53, 0x24, 0xa9, 0xc1, 0x4b, 0xd9, 0x23, 0xec, 0x1e,
0x8b, 0xda, 0x7e, 0x40, 0x0b, 0x73, 0x20, 0xb9, 0x9e, 0x73, 0x7c, 0xf1, 0x2a, 0x79, 0xe0, 0x07,
0x1e, 0x7f, 0x34, 0xda, 0x81, 0xc9, 0x5f, 0xf3, 0xbd, 0xa2, 0x71, 0x26, 0x8b, 0x97, 0x37, 0xe1,
0xa8, 0x88, 0xac, 0x2e, 0x53, 0x3f, 0xa8, 0xe0, 0x25, 0xb9, 0xa0, 0x1c, 0xca, 0xc3, 0xc9, 0x1f,
0xc4, 0x5b, 0x70, 0x9c, 0xc6, 0xb1, 0xdf, 0x0c, 0x98, 0xa7, 0x79, 0xcd, 0x4c, 0xcc, 0x6b, 0xf0,
0x68, 0xda, 0x44, 0xc8, 0x1d, 0xca, 0xe6, 0x9a, 0x24, 0xdf, 0x46, 0x70, 0x6a, 0x28, 0x93, 0xcc,
0xff, 0x90, 0x91, 0xce, 0x44, 0x8b, 0xef, 0xee, 0x31, 0xaf, 0xd3, 0x62, 0xba, 0x95, 0xd6, 0xb4,
0xf8, 0xcd, 0xeb, 0xa4, 0x37, 0xa0, 0xd2, 0x69, 0x46, 0xe3, 0x65, 0x80, 0x36, 0x0d, 0x3a, 0xb4,
0x25, 0x21, 0xcc, 0x49, 0x08, 0xc6, 0x0a, 0x59, 0x02, 0x7b, 0xd8, 0xf5, 0xa9, 0xb6, 0xf4, 0x57,
0x08, 0x8e, 0xe9, 0xd4, 0xa4, 0xee, 0x67, 0x15, 0x8e, 0x1b, 0x66, 0xd8, 0xe9, 0x5f, 0xd5, 0xe0,
0xf2, 0x98, 0xb4, 0xa3, 0xef, 0x79, 0x36, 0x3f, 0x27, 0xe9, 0xe6, 0x26, 0x1d, 0x13, 0xe7, 0xfd,
0x2c, 0x50, 0xc9, 0xb7, 0xc0, 0xda, 0xa6, 0x01, 0x6d, 0x32, 0x2f, 0x03, 0x9e, 0x39, 0xc9, 0x37,
0xcc, 0xd6, 0xeb, 0xd0, 0x8d, 0x4e, 0x56, 0xf6, 0xfd, 0x46, 0x43, 0xb5, 0x71, 0xf5, 0x7f, 0x2c,
0x03, 0x36, 0x2f, 0x95, 0x45, 0x5d, 0xdf, 0x65, 0xf8, 0x07, 0x08, 0xe6, 0x44, 0x95, 0xc1, 0x67,
0x47, 0xf9, 0x90, 0x34, 0xae, 0x3d, 0xbd, 0x77, 0xb4, 0x90, 0x46, 0x96, 0xde, 0xfb, 0xdb, 0x3f,
0x7f, 0x38, 0x73, 0x1a, 0xbf, 0x28, 0x27, 0x72, 0xdd, 0xeb, 0xe6, 0x74, 0x2c, 0xc6, 0xdf, 0x45,
0x80, 0x55, 0xe9, 0x33, 0x46, 0x2f, 0xf8, 0xca, 0x28, 0x88, 0x43, 0x46, 0x34, 0xf6, 0x59, 0x23,
0x0d, 0x55, 0x5d, 0x1e, 0x31, 0x91, 0x74, 0xe4, 0x06, 0x09, 0x60, 0x4d, 0x02, 0x38, 0x8f, 0xc9,
0x30, 0x00, 0xb5, 0xc7, 0xe2, 0xd2, 0x9f, 0xd4, 0x58, 0x2a, 0xf7, 0xe7, 0x08, 0xe6, 0x1f, 0xc8,
0xc7, 0xd7, 0x18, 0x23, 0xed, 0x4e, 0xcd, 0x48, 0x52, 0x9c, 0x44, 0x4b, 0xce, 0x49, 0xa4, 0x67,
0xf1, 0x19, 0x8d, 0x34, 0x4e, 0x22, 0x46, 0xdb, 0x39, 0xc0, 0xd7, 0x10, 0xfe, 0x10, 0xc1, 0x42,
0x3a, 0x9c, 0xc1, 0x17, 0x46, 0xa1, 0xcc, 0x0d, 0x6f, 0xec, 0xe9, 0x4d, 0x3a, 0xc8, 0x65, 0x89,
0xf1, 0x1c, 0x19, 0x7a, 0x9d, 0x1b, 0xb9, 0x39, 0xc8, 0xfb, 0x08, 0x66, 0x37, 0xd9, 0x58, 0x7f,
0x9b, 0x22, 0xb8, 0x03, 0x06, 0x1c, 0x72, 0xd5, 0xf8, 0x03, 0x04, 0x2f, 0x6d, 0xb2, 0x64, 0x78,
0x2e, 0xc7, 0xab, 0xe3, 0x13, 0xac, 0x72, 0xbb, 0x2b, 0x13, 0xec, 0xcc, 0x92, 0x58, 0x4d, 0x22,
0xbb, 0x8c, 0x2f, 0x15, 0x39, 0xa1, 0x68, 0x88, 0x1f, 0x29, 0x1c, 0x7f, 0x46, 0x70, 0x62, 0x70,
0xc8, 0x89, 0xf3, 0xd9, 0x7f, 0xe8, 0x0c, 0xd4, 0xde, 0x39, 0x6c, 0x42, 0xc9, 0x33, 0x25, 0xb7,
0x24, 0xf2, 0x57, 0xf1, 0x2b, 0x45, 0xc8, 0xf5, 0xdc, 0x27, 0xae, 0x3d, 0xd6, 0x9f, 0x4f, 0xe4,
0x1c, 0x5d, 0xc2, 0x7e, 0x0f, 0xc1, 0x91, 0x4d, 0x96, 0x6c, 0x67, 0x63, 0x8f, 0x91, 0x6e, 0x9b,
0x9b, 0x6b, 0xda, 0x4b, 0x55, 0x63, 0xdc, 0xad, 0x7f, 0xca, 0x4c, 0xba, 0x2e, 0x81, 0x5d, 0xc2,
0x17, 0x8a, 0x80, 0xf5, 0x47, 0x2d, 0xbf, 0x47, 0xb0, 0x90, 0x8e, 0x15, 0x46, 0x8b, 0xcf, 0x0d,
0x0d, 0xa7, 0xe9, 0x98, 0xb7, 0x25, 0xd6, 0xcf, 0xdb, 0xd7, 0x86, 0x63, 0x35, 0xcf, 0x6b, 0xab,
0x55, 0xa5, 0x02, 0xf9, 0x88, 0xfa, 0x08, 0x01, 0xf4, 0x47, 0x23, 0xf8, 0x72, 0xb1, 0x1e, 0xc6,
0xf8, 0xc4, 0x9e, 0xee, 0x70, 0x84, 0x54, 0xa5, 0x3e, 0xab, 0xf6, 0x4a, 0xa1, 0x3b, 0x87, 0xcc,
0xdd, 0x48, 0xc7, 0x28, 0x3f, 0x43, 0x30, 0x2f, 0x7b, 0x71, 0x7c, 0x7e, 0x14, 0x66, 0xb3, 0x55,
0x9f, 0xa6, 0xe9, 0x2f, 0x4a, 0xa8, 0x2b, 0xf5, 0xa2, 0x9c, 0xb0, 0x81, 0xd6, 0x70, 0x17, 0x16,
0xd2, 0xde, 0x79, 0xb4, 0x7b, 0xe4, 0x7a, 0x6b, 0x7b, 0xa5, 0xa0, 0x46, 0xa5, 0x1e, 0xaa, 0xd2,
0xd1, 0xda, 0xb8, 0x74, 0x34, 0x27, 0x32, 0x06, 0x3e, 0x57, 0x94, 0x4f, 0xfe, 0x0f, 0x86, 0xb9,
0x22, 0xd1, 0x5d, 0x20, 0x2b, 0xe3, 0x52, 0x92, 0xb0, 0xce, 0x8f, 0x10, 0x9c, 0x18, 0x7c, 0xd2,
0xe0, 0x33, 0x03, 0xe9, 0xc8, 0x7c, 0xa3, 0xd9, 0x79, 0x2b, 0x8e, 0x7a, 0x0e, 0x91, 0x2f, 0x48,
0x14, 0x1b, 0xf8, 0xe6, 0xd8, 0xc8, 0xd8, 0xd1, 0x01, 0x2d, 0x18, 0xad, 0xf7, 0x27, 0xac, 0xbf,
0x41, 0x70, 0x44, 0xf3, 0xbd, 0x17, 0x31, 0x56, 0x0c, 0x6b, 0x7a, 0x81, 0x20, 0x64, 0x91, 0xcf,
0x4a, 0xf8, 0x9f, 0xc1, 0x37, 0x26, 0x84, 0xaf, 0x61, 0xaf, 0x27, 0x02, 0xe9, 0x1f, 0x10, 0x9c,
0x7c, 0x90, 0xfa, 0xfd, 0x27, 0x84, 0xff, 0x75, 0x89, 0xff, 0x73, 0xf8, 0xd5, 0x82, 0x27, 0xc7,
0x38, 0x35, 0xae, 0x21, 0xfc, 0x4b, 0x04, 0x25, 0x3d, 0x9d, 0xc4, 0x97, 0x46, 0x06, 0x46, 0x7e,
0x7e, 0x39, 0x4d, 0x67, 0x56, 0xf5, 0x95, 0x9c, 0x2f, 0xac, 0x52, 0x4a, 0xbe, 0x70, 0xe8, 0xf7,
0x11, 0xe0, 0xac, 0xd7, 0xc8, 0xba, 0x0f, 0x7c, 0x31, 0x27, 0x6a, 0x64, 0x53, 0x69, 0x5f, 0x1a,
0xbb, 0x2f, 0x5f, 0xa5, 0xd6, 0x0a, 0xab, 0x14, 0xcf, 0xe4, 0x7f, 0x0f, 0x41, 0x65, 0x93, 0x65,
0xcf, 0xe1, 0x02, 0x5b, 0xe6, 0x47, 0xb3, 0xf6, 0xea, 0xf8, 0x8d, 0x0a, 0xd1, 0x55, 0x89, 0xe8,
0x22, 0x2e, 0x36, 0x95, 0x06, 0xf0, 0x13, 0x04, 0x47, 0xef, 0x9a, 0x2e, 0x8a, 0xaf, 0x8e, 0x93,
0x94, 0xcb, 0xe4, 0x93, 0xe3, 0xfa, 0xb4, 0xc4, 0xb5, 0x4e, 0x26, 0xc2, 0xb5, 0xa1, 0x66, 0xa4,
0x3f, 0x45, 0xf0, 0x82, 0xd9, 0x3f, 0xa8, 0x69, 0xda, 0xff, 0x6a, 0xb7, 0x82, 0xa1, 0x1c, 0xb9,
0x21, 0xf1, 0x55, 0xf1, 0xd5, 0x49, 0xf0, 0xd5, 0xd4, 0x88, 0x0d, 0xff, 0x18, 0xc1, 0x49, 0x39,
0xab, 0x34, 0x19, 0x0f, 0x94, 0x98, 0x51, 0x93, 0xcd, 0x09, 0x4a, 0x8c, 0xca, 0x3f, 0xe4, 0x99,
0x40, 0x6d, 0xa8, 0x39, 0x24, 0xfe, 0x3e, 0x82, 0x63, 0xba, 0xa8, 0xa9, 0xdb, 0x5d, 0x1f, 0x67,
0xb8, 0x67, 0x2d, 0x82, 0xca, 0xdd, 0xd6, 0x26, 0x73, 0xb7, 0x0f, 0x11, 0x2c, 0xaa, 0x59, 0x62,
0xc1, 0x53, 0xc1, 0x18, 0x36, 0xda, 0xa7, 0x72, 0xbb, 0xf4, 0x20, 0x8b, 0x7c, 0x4d, 0x8a, 0xbd,
0x8f, 0x6b, 0x45, 0x62, 0x43, 0xee, 0xc5, 0xb5, 0xc7, 0x6a, 0x8a, 0xf4, 0xa4, 0xd6, 0xe2, 0xcd,
0xf8, 0x1d, 0x82, 0x0b, 0x0b, 0xa2, 0xd8, 0x73, 0x0d, 0xbd, 0xf6, 0xc5, 0x3f, 0x3d, 0x5d, 0x46,
0x1f, 0x3f, 0x5d, 0x46, 0x7f, 0x7f, 0xba, 0x8c, 0xde, 0xb9, 0x39, 0xd9, 0x3f, 0x43, 0xdc, 0x96,
0xcf, 0x82, 0xc4, 0x64, 0xfb, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xaf, 0x09, 0x16, 0xc3, 0xff,
0x22, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -3305,6 +3315,15 @@ func (m *ApplicationQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Project) > 0 {
for iNdEx := len(m.Project) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.Project[iNdEx])
copy(dAtA[i:], m.Project[iNdEx])
i = encodeVarintApplication(dAtA, i, uint64(len(m.Project[iNdEx])))
i--
dAtA[i] = 0x42
}
}
if m.Repo != nil {
i -= len(*m.Repo)
copy(dAtA[i:], *m.Repo)
@@ -5105,6 +5124,12 @@ func (m *ApplicationQuery) Size() (n int) {
l = len(*m.Repo)
n += 1 + l + sovApplication(uint64(l))
}
if len(m.Project) > 0 {
for _, s := range m.Project {
l = len(s)
n += 1 + l + sovApplication(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
@@ -6092,6 +6117,38 @@ func (m *ApplicationQuery) Unmarshal(dAtA []byte) error {
s := string(dAtA[iNdEx:postIndex])
m.Repo = &s
iNdEx = postIndex
case 8:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowApplication
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthApplication
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthApplication
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Project = append(m.Project, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipApplication(dAtA[iNdEx:])

View File

@@ -18,8 +18,8 @@ const (
AppProjectShortName string = "appproject"
AppProjectFullName string = AppProjectPlural + "." + Group
// AppProject constants
ApplicationSetKind string = "Applicationset"
// ApplicationSet constants
ApplicationSetKind string = "ApplicationSet"
ApplicationSetSingular string = "applicationset"
ApplicationSetPlural string = "applicationsets"
ApplicationSetShortName string = "appset"

View File

@@ -496,7 +496,7 @@ const (
// prefix "Info" means informational condition
type ApplicationSetConditionType string
//ErrorOccurred / ParametersGenerated / TemplateRendered / ResourcesUpToDate
// ErrorOccurred / ParametersGenerated / TemplateRendered / ResourcesUpToDate
const (
ApplicationSetConditionErrorOccurred ApplicationSetConditionType = "ErrorOccurred"
ApplicationSetConditionParametersGenerated ApplicationSetConditionType = "ParametersGenerated"

View File

@@ -1241,7 +1241,7 @@ func TestGetAppDetailsKustomize(t *testing.T) {
assert.Equal(t, "Kustomize", res.Type)
assert.NotNil(t, res.Kustomize)
assert.EqualValues(t, []string{"nginx:1.15.4", "k8s.gcr.io/nginx-slim:0.8"}, res.Kustomize.Images)
assert.EqualValues(t, []string{"nginx:1.15.4", "registry.k8s.io/nginx-slim:0.8"}, res.Kustomize.Images)
}
func TestGetHelmCharts(t *testing.T) {

View File

@@ -25,7 +25,7 @@ spec:
name: daemonset
spec:
containers:
- image: k8s.gcr.io/nginx-slim:0.8
- image: registry.k8s.io/nginx-slim:0.8
imagePullPolicy: IfNotPresent
name: nginx
resources: {}

View File

@@ -23,7 +23,7 @@ spec:
name: daemonset
spec:
containers:
- image: k8s.gcr.io/nginx-slim:0.8
- image: registry.k8s.io/nginx-slim:0.8
imagePullPolicy: IfNotPresent
name: nginx
resources: {}

View File

@@ -26,7 +26,7 @@ spec:
kubectl.kubernetes.io/restartedAt: "0001-01-01T00:00:00Z"
spec:
containers:
- image: k8s.gcr.io/nginx-slim:0.8
- image: registry.k8s.io/nginx-slim:0.8
imagePullPolicy: IfNotPresent
name: nginx
resources: {}

View File

@@ -24,7 +24,7 @@ spec:
app: statefulset
spec:
containers:
- image: k8s.gcr.io/nginx-slim:0.8
- image: registry.k8s.io/nginx-slim:0.8
imagePullPolicy: IfNotPresent
name: nginx
resources: {}

View File

@@ -1,4 +1,55 @@
health_status = {}
-- Can't use standard lib, math.huge equivalent
infinity = 2^1024-1
local function executor_range_api()
min_executor_instances = 0
max_executor_instances = infinity
if obj.spec.dynamicAllocation.maxExecutors then
max_executor_instances = obj.spec.dynamicAllocation.maxExecutors
end
if obj.spec.dynamicAllocation.minExecutors then
min_executor_instances = obj.spec.dynamicAllocation.minExecutors
end
return min_executor_instances, max_executor_instances
end
local function maybe_executor_range_spark_conf()
min_executor_instances = 0
max_executor_instances = infinity
if obj.spec.sparkConf["spark.streaming.dynamicAllocation.enabled"] ~= nil and
obj.spec.sparkConf["spark.streaming.dynamicAllocation.enabled"] == "true" then
if(obj.spec.sparkConf["spark.streaming.dynamicAllocation.maxExecutors"] ~= nil) then
max_executor_instances = tonumber(obj.spec.sparkConf["spark.streaming.dynamicAllocation.maxExecutors"])
end
if(obj.spec.sparkConf["spark.streaming.dynamicAllocation.minExecutors"] ~= nil) then
min_executor_instances = tonumber(obj.spec.sparkConf["spark.streaming.dynamicAllocation.minExecutors"])
end
return min_executor_instances, max_executor_instances
elseif obj.spec.sparkConf["spark.dynamicAllocation.enabled"] ~= nil and
obj.spec.sparkConf["spark.dynamicAllocation.enabled"] == "true" then
if(obj.spec.sparkConf["spark.dynamicAllocation.maxExecutors"] ~= nil) then
max_executor_instances = tonumber(obj.spec.sparkConf["spark.dynamicAllocation.maxExecutors"])
end
if(obj.spec.sparkConf["spark.dynamicAllocation.minExecutors"] ~= nil) then
min_executor_instances = tonumber(obj.spec.sparkConf["spark.dynamicAllocation.minExecutors"])
end
return min_executor_instances, max_executor_instances
else
return nil
end
end
local function maybe_executor_range()
if obj.spec["dynamicAllocation"] and obj.spec.dynamicAllocation.enabled then
return executor_range_api()
elseif obj.spec["sparkConf"] ~= nil then
return maybe_executor_range_spark_conf()
else
return nil
end
end
if obj.status ~= nil then
if obj.status.applicationState.state ~= nil then
if obj.status.applicationState.state == "" then
@@ -19,6 +70,13 @@ if obj.status ~= nil then
health_status.status = "Healthy"
health_status.message = "SparkApplication is Running"
return health_status
elseif maybe_executor_range() then
min_executor_instances, max_executor_instances = maybe_executor_range()
if count >= min_executor_instances and count <= max_executor_instances then
health_status.status = "Healthy"
health_status.message = "SparkApplication is Running"
return health_status
end
end
end
end
@@ -72,4 +130,4 @@ if obj.status ~= nil then
end
health_status.status = "Progressing"
health_status.message = "Waiting for Executor pods"
return health_status
return health_status

View File

@@ -11,3 +11,15 @@ tests:
status: Healthy
message: "SparkApplication is Running"
inputPath: testdata/healthy.yaml
- healthStatus:
status: Healthy
message: "SparkApplication is Running"
inputPath: testdata/healthy_dynamic_alloc.yaml
- healthStatus:
status: Healthy
message: "SparkApplication is Running"
inputPath: testdata/healthy_dynamic_alloc_dstream.yaml
- healthStatus:
status: Healthy
message: "SparkApplication is Running"
inputPath: testdata/healthy_dynamic_alloc_operator_api.yaml

View File

@@ -0,0 +1,37 @@
apiVersion: sparkoperator.k8s.io/v1beta2
kind: SparkApplication
metadata:
generation: 4
labels:
argocd.argoproj.io/instance: spark-job
name: spark-job-app
namespace: spark-cluster
resourceVersion: "31812990"
uid: bfee52b0-74ca-4465-8005-f6643097ed64
spec:
executor:
instances: 4
sparkConf:
spark.dynamicAllocation.enabled: 'true'
spark.dynamicAllocation.maxExecutors: '10'
spark.dynamicAllocation.minExecutors: '2'
status:
applicationState:
state: RUNNING
driverInfo:
podName: ingestion-datalake-news-app-driver
webUIAddress: 172.20.207.161:4040
webUIPort: 4040
webUIServiceName: ingestion-datalake-news-app-ui-svc
executionAttempts: 13
executorState:
ingestion-datalake-news-app-1591613851251-exec-1: RUNNING
ingestion-datalake-news-app-1591613851251-exec-2: RUNNING
ingestion-datalake-news-app-1591613851251-exec-4: RUNNING
ingestion-datalake-news-app-1591613851251-exec-5: RUNNING
ingestion-datalake-news-app-1591613851251-exec-6: RUNNING
lastSubmissionAttemptTime: "2020-06-08T10:57:32Z"
sparkApplicationId: spark-a5920b2a5aa04d22a737c60759b5bf82
submissionAttempts: 1
submissionID: 3e713ec8-9f6c-4e78-ac28-749797c846f0
terminationTime: null

View File

@@ -0,0 +1,35 @@
apiVersion: sparkoperator.k8s.io/v1beta2
kind: SparkApplication
metadata:
generation: 4
labels:
argocd.argoproj.io/instance: spark-job
name: spark-job-app
namespace: spark-cluster
resourceVersion: "31812990"
uid: bfee52b0-74ca-4465-8005-f6643097ed64
spec:
executor:
instances: 4
sparkConf:
spark.streaming.dynamicAllocation.enabled: 'true'
spark.streaming.dynamicAllocation.maxExecutors: '10'
spark.streaming.dynamicAllocation.minExecutors: '2'
status:
applicationState:
state: RUNNING
driverInfo:
podName: ingestion-datalake-news-app-driver
webUIAddress: 172.20.207.161:4040
webUIPort: 4040
webUIServiceName: ingestion-datalake-news-app-ui-svc
executionAttempts: 13
executorState:
ingestion-datalake-news-app-1591613851251-exec-1: RUNNING
ingestion-datalake-news-app-1591613851251-exec-4: RUNNING
ingestion-datalake-news-app-1591613851251-exec-6: RUNNING
lastSubmissionAttemptTime: "2020-06-08T10:57:32Z"
sparkApplicationId: spark-a5920b2a5aa04d22a737c60759b5bf82
submissionAttempts: 1
submissionID: 3e713ec8-9f6c-4e78-ac28-749797c846f0
terminationTime: null

View File

@@ -0,0 +1,38 @@
apiVersion: sparkoperator.k8s.io/v1beta2
kind: SparkApplication
metadata:
generation: 4
labels:
argocd.argoproj.io/instance: spark-job
name: spark-job-app
namespace: spark-cluster
resourceVersion: "31812990"
uid: bfee52b0-74ca-4465-8005-f6643097ed64
spec:
executor:
instances: 4
dynamicAllocation:
enabled: true
initialExecutors: 2
minExecutors: 2
maxExecutors: 10
status:
applicationState:
state: RUNNING
driverInfo:
podName: ingestion-datalake-news-app-driver
webUIAddress: 172.20.207.161:4040
webUIPort: 4040
webUIServiceName: ingestion-datalake-news-app-ui-svc
executionAttempts: 13
executorState:
ingestion-datalake-news-app-1591613851251-exec-1: RUNNING
ingestion-datalake-news-app-1591613851251-exec-2: RUNNING
ingestion-datalake-news-app-1591613851251-exec-4: RUNNING
ingestion-datalake-news-app-1591613851251-exec-5: RUNNING
ingestion-datalake-news-app-1591613851251-exec-6: RUNNING
lastSubmissionAttemptTime: "2020-06-08T10:57:32Z"
sparkApplicationId: spark-a5920b2a5aa04d22a737c60759b5bf82
submissionAttempts: 1
submissionID: 3e713ec8-9f6c-4e78-ac28-749797c846f0
terminationTime: null

View File

@@ -67,7 +67,8 @@ const (
)
var (
watchAPIBufferSize = env.ParseNumFromEnv(argocommon.EnvWatchAPIBufferSize, 1000, 0, math.MaxInt32)
watchAPIBufferSize = env.ParseNumFromEnv(argocommon.EnvWatchAPIBufferSize, 1000, 0, math.MaxInt32)
permissionDeniedErr = status.Error(codes.PermissionDenied, "permission denied")
)
// Server provides an Application service
@@ -77,7 +78,7 @@ type Server struct {
appclientset appclientset.Interface
appLister applisters.ApplicationNamespaceLister
appInformer cache.SharedIndexInformer
appBroadcaster *broadcasterHandler
appBroadcaster Broadcaster
repoClientset apiclient.Clientset
kubectl kube.Kubectl
db db.ArgoDB
@@ -96,6 +97,7 @@ func NewServer(
appclientset appclientset.Interface,
appLister applisters.ApplicationNamespaceLister,
appInformer cache.SharedIndexInformer,
appBroadcaster Broadcaster,
repoClientset apiclient.Clientset,
cache *servercache.Cache,
kubectl kube.Kubectl,
@@ -105,7 +107,9 @@ func NewServer(
settingsMgr *settings.SettingsManager,
projInformer cache.SharedIndexInformer,
) (application.ApplicationServiceServer, AppResourceTreeFn) {
appBroadcaster := &broadcasterHandler{}
if appBroadcaster == nil {
appBroadcaster = &broadcasterHandler{}
}
appInformer.AddEventHandler(appBroadcaster)
s := &Server{
ns: namespace,
@@ -127,6 +131,57 @@ func NewServer(
return s, s.GetAppResources
}
// getAppEnforceRBAC gets the Application with the given name in the given namespace. If no namespace is
// specified, the Application is fetched from the default namespace (the one in which the API server is running).
//
// If the Application does not exist, then we have no way of determining if the user would have had access to get that
// Application. Verifying access requires knowing the Application's name, namespace, and project. The user may specify,
// at minimum, the Application name.
//
// So to prevent a malicious user from inferring the existence or absense of the Application or namespace, we respond
// "permission denied" if the Application does not exist.
func (s *Server) getAppEnforceRBAC(ctx context.Context, action, name string, getApp func() (*appv1.Application, error)) (*appv1.Application, error) {
logCtx := log.WithFields(map[string]interface{}{
"application": name,
})
a, err := getApp()
if err != nil {
if apierr.IsNotFound(err) {
logCtx.Warn("application does not exist")
return nil, permissionDeniedErr
}
logCtx.Errorf("failed to get application: %s", err)
return nil, permissionDeniedErr
}
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, action, apputil.AppRBACName(*a)); err != nil {
logCtx.WithFields(map[string]interface{}{
"project": a.Spec.Project,
}).Warnf("user tried to %s application which they do not have access to: %s", action, err)
return nil, permissionDeniedErr
}
return a, nil
}
// getApplicationEnforceRBACInformer uses an informer to get an Application. If the app does not exist, permission is
// denied, or any other error occurs when getting the app, we return a permission denied error to obscure any sensitive
// information.
func (s *Server) getApplicationEnforceRBACInformer(ctx context.Context, action, name string) (*appv1.Application, error) {
return s.getAppEnforceRBAC(ctx, action, name, func() (*appv1.Application, error) {
return s.appLister.Get(name)
})
}
// getApplicationEnforceRBACClient uses a client to get an Application. If the app does not exist, permission is denied,
// or any other error occurs when getting the app, we return a permission denied error to obscure any sensitive
// information.
func (s *Server) getApplicationEnforceRBACClient(ctx context.Context, action, name, resourceVersion string) (*appv1.Application, error) {
return s.getAppEnforceRBAC(ctx, action, name, func() (*appv1.Application, error) {
return s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(ctx, name, metav1.GetOptions{
ResourceVersion: resourceVersion,
})
})
}
// List returns list of applications
func (s *Server) List(ctx context.Context, q *application.ApplicationQuery) (*appv1.ApplicationList, error) {
labelsMap, err := labels.ConvertSelectorToLabelsMap(q.GetSelector())
@@ -150,8 +205,8 @@ func (s *Server) List(ctx context.Context, q *application.ApplicationQuery) (*ap
}
}
// Filter applications by name
newItems = argoutil.FilterByProjects(newItems, q.Projects)
// Filter applications by projects
newItems = argoutil.FilterByProjects(newItems, getProjectsFromApplicationQuery(*q))
// Filter applications by source repo URL
newItems = argoutil.FilterByRepo(newItems, q.GetRepo())
@@ -291,11 +346,11 @@ func (s *Server) queryRepoServer(ctx context.Context, a *v1alpha1.Application, a
// GetManifests returns application manifests
func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationManifestQuery) (*apiclient.ManifestResponse, error) {
a, err := s.appLister.Get(*q.Name)
if err != nil {
return nil, fmt.Errorf("error getting application: %w", err)
if q.Name == nil || *q.Name == "" {
return nil, fmt.Errorf("invalid request: application name is missing")
}
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, apputil.AppRBACName(*a)); err != nil {
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetName())
if err != nil {
return nil, err
}
@@ -381,17 +436,13 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan
// Get returns an application by name
func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*appv1.Application, error) {
appName := q.GetName()
// We must use a client Get instead of an informer Get, because it's common to call Get immediately
// following a Watch (which is not yet powered by an informer), and the Get must reflect what was
// previously seen by the client.
a, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(ctx, q.GetName(), metav1.GetOptions{
ResourceVersion: q.GetResourceVersion(),
})
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, appName, q.GetResourceVersion())
if err != nil {
return nil, fmt.Errorf("error getting application: %w", err)
}
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, apputil.AppRBACName(*a)); err != nil {
return nil, err
}
if q.Refresh == nil {
@@ -469,11 +520,8 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*app
// ListResourceEvents returns a list of event resources
func (s *Server) ListResourceEvents(ctx context.Context, q *application.ApplicationResourceEventsQuery) (*v1.EventList, error) {
a, err := s.appLister.Get(*q.Name)
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetName())
if err != nil {
return nil, fmt.Errorf("error getting application: %w", err)
}
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, apputil.AppRBACName(*a)); err != nil {
return nil, err
}
var (
@@ -533,13 +581,13 @@ func (s *Server) ListResourceEvents(ctx context.Context, q *application.Applicat
return list, nil
}
func (s *Server) validateAndUpdateApp(ctx context.Context, newApp *appv1.Application, merge bool, validate bool) (*appv1.Application, error) {
func (s *Server) validateAndUpdateApp(ctx context.Context, newApp *appv1.Application, merge bool, validate bool, action string) (*appv1.Application, error) {
s.projectLock.RLock(newApp.Spec.GetProject())
defer s.projectLock.RUnlock(newApp.Spec.GetProject())
app, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(ctx, newApp.Name, metav1.GetOptions{})
app, err := s.getApplicationEnforceRBACClient(ctx, action, newApp.Name, "")
if err != nil {
return nil, fmt.Errorf("error getting application: %w", err)
return nil, err
}
err = s.validateAndNormalizeApp(ctx, newApp, validate)
@@ -641,7 +689,7 @@ func (s *Server) Update(ctx context.Context, q *application.ApplicationUpdateReq
if q.Validate != nil {
validate = *q.Validate
}
return s.validateAndUpdateApp(ctx, q.Application, false, validate)
return s.validateAndUpdateApp(ctx, q.Application, false, validate, rbacpolicy.ActionUpdate)
}
// UpdateSpec updates an application spec and filters out any invalid parameter overrides
@@ -649,11 +697,8 @@ func (s *Server) UpdateSpec(ctx context.Context, q *application.ApplicationUpdat
if q.GetSpec() == nil {
return nil, fmt.Errorf("error updating application spec: spec is nil in request")
}
a, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(ctx, *q.Name, metav1.GetOptions{})
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionUpdate, q.GetName(), "")
if err != nil {
return nil, fmt.Errorf("error getting application: %w", err)
}
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, apputil.AppRBACName(*a)); err != nil {
return nil, err
}
a.Spec = *q.GetSpec()
@@ -661,7 +706,7 @@ func (s *Server) UpdateSpec(ctx context.Context, q *application.ApplicationUpdat
if q.Validate != nil {
validate = *q.Validate
}
a, err = s.validateAndUpdateApp(ctx, a, false, validate)
a, err = s.validateAndUpdateApp(ctx, a, false, validate, rbacpolicy.ActionUpdate)
if err != nil {
return nil, fmt.Errorf("error validating and updating app: %w", err)
}
@@ -670,10 +715,9 @@ func (s *Server) UpdateSpec(ctx context.Context, q *application.ApplicationUpdat
// Patch patches an application
func (s *Server) Patch(ctx context.Context, q *application.ApplicationPatchRequest) (*appv1.Application, error) {
app, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(ctx, *q.Name, metav1.GetOptions{})
app, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, q.GetName(), "")
if err != nil {
return nil, fmt.Errorf("error getting application: %w", err)
return nil, err
}
if err = s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, apputil.AppRBACName(*app)); err != nil {
@@ -711,14 +755,15 @@ func (s *Server) Patch(ctx context.Context, q *application.ApplicationPatchReque
if err != nil {
return nil, fmt.Errorf("error unmarshaling patched app: %w", err)
}
return s.validateAndUpdateApp(ctx, newApp, false, true)
return s.validateAndUpdateApp(ctx, newApp, false, true, rbacpolicy.ActionUpdate)
}
// Delete removes an application and all associated resources
func (s *Server) Delete(ctx context.Context, q *application.ApplicationDeleteRequest) (*application.ApplicationResponse, error) {
a, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(ctx, *q.Name, metav1.GetOptions{})
appName := q.GetName()
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, appName, "")
if err != nil {
return nil, fmt.Errorf("error getting application: %w", err)
return nil, err
}
s.projectLock.RLock(a.Spec.Project)
@@ -782,8 +827,8 @@ func (s *Server) Watch(q *application.ApplicationQuery, ws application.Applicati
logCtx = logCtx.WithField("application", *q.Name)
}
projects := map[string]bool{}
for i := range q.Projects {
projects[q.Projects[i]] = true
for _, project := range getProjectsFromApplicationQuery(*q) {
projects[project] = true
}
claims := ws.Context().Value("claims")
selector, err := labels.Parse(q.GetSelector())
@@ -859,7 +904,9 @@ func (s *Server) validateAndNormalizeApp(ctx context.Context, app *appv1.Applica
proj, err := argo.GetAppProject(&app.Spec, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx)
if err != nil {
if apierr.IsNotFound(err) {
return status.Errorf(codes.InvalidArgument, "application references project %s which does not exist", app.Spec.Project)
// Offer no hint that the project does not exist.
log.Warnf("User attempted to create/update application in non-existent project %q", app.Spec.Project)
return permissionDeniedErr
}
return fmt.Errorf("error getting application's project: %w", err)
}
@@ -966,20 +1013,16 @@ func (s *Server) GetAppResources(ctx context.Context, a *appv1.Application) (*ap
return s.cache.GetAppResourcesTree(a.Name, &tree)
})
if err != nil {
return &tree, fmt.Errorf("error getting cached app state: %w", err)
return &tree, fmt.Errorf("error getting cached app resource tree: %w", err)
}
return &tree, nil
}
func (s *Server) getAppLiveResource(ctx context.Context, action string, q *application.ApplicationResourceRequest) (*appv1.ResourceNode, *rest.Config, *appv1.Application, error) {
a, err := s.appLister.Get(*q.Name)
a, err := s.getApplicationEnforceRBACInformer(ctx, action, q.GetName())
if err != nil {
return nil, nil, nil, fmt.Errorf("error getting app by name: %w", err)
}
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, action, apputil.AppRBACName(*a)); err != nil {
return nil, nil, nil, err
}
tree, err := s.GetAppResources(ctx, a)
if err != nil {
return nil, nil, nil, fmt.Errorf("error getting app resources: %w", err)
@@ -999,7 +1042,7 @@ func (s *Server) getAppLiveResource(ctx context.Context, action string, q *appli
func (s *Server) GetResource(ctx context.Context, q *application.ApplicationResourceRequest) (*application.ApplicationResourceResponse, error) {
res, config, _, err := s.getAppLiveResource(ctx, rbacpolicy.ActionGet, q)
if err != nil {
return nil, fmt.Errorf("error getting app live resource: %w", err)
return nil, err
}
// make sure to use specified resource version if provided
@@ -1045,9 +1088,6 @@ func (s *Server) PatchResource(ctx context.Context, q *application.ApplicationRe
}
res, config, a, err := s.getAppLiveResource(ctx, rbacpolicy.ActionUpdate, resourceRequest)
if err != nil {
return nil, fmt.Errorf("error getting app live resource: %w", err)
}
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, apputil.AppRBACName(*a)); err != nil {
return nil, err
}
@@ -1059,6 +1099,9 @@ func (s *Server) PatchResource(ctx context.Context, q *application.ApplicationRe
}
return nil, fmt.Errorf("error patching resource: %w", err)
}
if manifest == nil {
return nil, fmt.Errorf("failed to patch resource: manifest was nil")
}
manifest, err = replaceSecretValues(manifest)
if err != nil {
return nil, fmt.Errorf("error replacing secret values: %w", err)
@@ -1086,9 +1129,6 @@ func (s *Server) DeleteResource(ctx context.Context, q *application.ApplicationR
}
res, config, a, err := s.getAppLiveResource(ctx, rbacpolicy.ActionDelete, resourceRequest)
if err != nil {
return nil, fmt.Errorf("error getting live resource for delete: %w", err)
}
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionDelete, apputil.AppRBACName(*a)); err != nil {
return nil, err
}
var deleteOption metav1.DeleteOptions
@@ -1112,23 +1152,16 @@ func (s *Server) DeleteResource(ctx context.Context, q *application.ApplicationR
}
func (s *Server) ResourceTree(ctx context.Context, q *application.ResourcesQuery) (*appv1.ApplicationTree, error) {
a, err := s.appLister.Get(q.GetApplicationName())
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetApplicationName())
if err != nil {
return nil, fmt.Errorf("error getting application by name: %w", err)
}
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, apputil.AppRBACName(*a)); err != nil {
return nil, err
}
return s.GetAppResources(ctx, a)
}
func (s *Server) WatchResourceTree(q *application.ResourcesQuery, ws application.ApplicationService_WatchResourceTreeServer) error {
a, err := s.appLister.Get(q.GetApplicationName())
_, err := s.getApplicationEnforceRBACInformer(ws.Context(), rbacpolicy.ActionGet, q.GetApplicationName())
if err != nil {
return fmt.Errorf("error getting application by name: %w", err)
}
if err := s.enf.EnforceErr(ws.Context().Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, apputil.AppRBACName(*a)); err != nil {
return err
}
@@ -1143,11 +1176,8 @@ func (s *Server) WatchResourceTree(q *application.ResourcesQuery, ws application
}
func (s *Server) RevisionMetadata(ctx context.Context, q *application.RevisionMetadataQuery) (*v1alpha1.RevisionMetadata, error) {
a, err := s.appLister.Get(q.GetName())
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetName())
if err != nil {
return nil, fmt.Errorf("error getting app by name: %w", err)
}
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, apputil.AppRBACName(*a)); err != nil {
return nil, err
}
repo, err := s.db.GetRepository(ctx, a.Spec.Source.RepoURL)
@@ -1180,19 +1210,16 @@ func isMatchingResource(q *application.ResourcesQuery, key kube.ResourceKey) boo
}
func (s *Server) ManagedResources(ctx context.Context, q *application.ResourcesQuery) (*application.ManagedResourcesResponse, error) {
a, err := s.appLister.Get(*q.ApplicationName)
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetApplicationName())
if err != nil {
return nil, fmt.Errorf("error getting application: %w", err)
}
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, apputil.AppRBACName(*a)); err != nil {
return nil, fmt.Errorf("error verifying rbac: %w", err)
return nil, err
}
items := make([]*appv1.ResourceDiff, 0)
err = s.getCachedAppState(ctx, a, func() error {
return s.cache.GetAppManagedResources(a.Name, &items)
})
if err != nil {
return nil, fmt.Errorf("error getting cached app state: %w", err)
return nil, fmt.Errorf("error getting cached app managed resources: %w", err)
}
res := &application.ManagedResourcesResponse{}
for i := range items {
@@ -1239,12 +1266,8 @@ func (s *Server) PodLogs(q *application.ApplicationPodLogsQuery, ws application.
}
}
a, err := s.appLister.Get(q.GetName())
a, err := s.getApplicationEnforceRBACInformer(ws.Context(), rbacpolicy.ActionGet, q.GetName())
if err != nil {
return fmt.Errorf("error getting application by name: %w", err)
}
if err := s.enf.EnforceErr(ws.Context().Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, apputil.AppRBACName(*a)); err != nil {
return err
}
@@ -1436,10 +1459,9 @@ func isTheSelectedOne(currentNode *appv1.ResourceNode, q *application.Applicatio
// Sync syncs an application to its target state
func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncRequest) (*appv1.Application, error) {
appIf := s.appclientset.ArgoprojV1alpha1().Applications(s.ns)
a, err := appIf.Get(ctx, *syncReq.Name, metav1.GetOptions{})
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, syncReq.GetName(), "")
if err != nil {
return nil, fmt.Errorf("error getting application by name: %w", err)
return nil, err
}
proj, err := argo.GetAppProject(&a.Spec, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), a.Namespace, s.settingsMgr, s.db, ctx)
@@ -1521,7 +1543,9 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR
op.Retry = *retry
}
a, err = argo.SetAppOperation(appIf, *syncReq.Name, &op)
appName := syncReq.GetName()
appIf := s.appclientset.ArgoprojV1alpha1().Applications(s.ns)
a, err = argo.SetAppOperation(appIf, appName, &op)
if err != nil {
return nil, fmt.Errorf("error setting app operation: %w", err)
}
@@ -1538,12 +1562,8 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR
}
func (s *Server) Rollback(ctx context.Context, rollbackReq *application.ApplicationRollbackRequest) (*appv1.Application, error) {
appIf := s.appclientset.ArgoprojV1alpha1().Applications(s.ns)
a, err := appIf.Get(ctx, *rollbackReq.Name, metav1.GetOptions{})
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionSync, rollbackReq.GetName(), "")
if err != nil {
return nil, fmt.Errorf("error getting application by name: %w", err)
}
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionSync, apputil.AppRBACName(*a)); err != nil {
return nil, err
}
if a.DeletionTimestamp != nil {
@@ -1585,7 +1605,9 @@ func (s *Server) Rollback(ctx context.Context, rollbackReq *application.Applicat
Source: &deploymentInfo.Source,
},
}
a, err = argo.SetAppOperation(appIf, *rollbackReq.Name, &op)
appName := rollbackReq.GetName()
appIf := s.appclientset.ArgoprojV1alpha1().Applications(s.ns)
a, err = argo.SetAppOperation(appIf, appName, &op)
if err != nil {
return nil, fmt.Errorf("error setting app operation: %w", err)
}
@@ -1632,11 +1654,9 @@ func (s *Server) resolveRevision(ctx context.Context, app *appv1.Application, sy
}
func (s *Server) TerminateOperation(ctx context.Context, termOpReq *application.OperationTerminateRequest) (*application.OperationTerminateResponse, error) {
a, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(ctx, *termOpReq.Name, metav1.GetOptions{})
appName := termOpReq.GetName()
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionSync, appName, "")
if err != nil {
return nil, fmt.Errorf("error getting application by name: %w", err)
}
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionSync, apputil.AppRBACName(*a)); err != nil {
return nil, err
}
@@ -1687,7 +1707,7 @@ func (s *Server) logResourceEvent(res *appv1.ResourceNode, ctx context.Context,
func (s *Server) ListResourceActions(ctx context.Context, q *application.ApplicationResourceRequest) (*application.ResourceActionsListResponse, error) {
res, config, _, err := s.getAppLiveResource(ctx, rbacpolicy.ActionGet, q)
if err != nil {
return nil, fmt.Errorf("error getting app live resource: %w", err)
return nil, err
}
obj, err := s.kubectl.GetResource(ctx, config, res.GroupKindVersion(), res.Name, res.Namespace)
if err != nil {
@@ -1742,7 +1762,7 @@ func (s *Server) RunResourceAction(ctx context.Context, q *application.ResourceA
actionRequest := fmt.Sprintf("%s/%s/%s/%s", rbacpolicy.ActionAction, q.GetGroup(), q.GetKind(), q.GetAction())
res, config, a, err := s.getAppLiveResource(ctx, actionRequest, resourceRequest)
if err != nil {
return nil, fmt.Errorf("error getting app live resource: %w", err)
return nil, err
}
liveObj, err := s.kubectl.GetResource(ctx, config, res.GroupKindVersion(), res.Name, res.Namespace)
if err != nil {
@@ -1869,13 +1889,8 @@ func (s *Server) plugins() ([]*v1alpha1.ConfigManagementPlugin, error) {
}
func (s *Server) GetApplicationSyncWindows(ctx context.Context, q *application.ApplicationSyncWindowsQuery) (*application.ApplicationSyncWindowsResponse, error) {
appIf := s.appclientset.ArgoprojV1alpha1().Applications(s.ns)
a, err := appIf.Get(ctx, *q.Name, metav1.GetOptions{})
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, q.GetName(), "")
if err != nil {
return nil, fmt.Errorf("error getting application by name: %w", err)
}
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, apputil.AppRBACName(*a)); err != nil {
return nil, err
}
@@ -1927,3 +1942,12 @@ func getPropagationPolicyFinalizer(policy string) string {
return ""
}
}
// getProjectFromApplicationQuery gets the project names from a query. If the legacy "project" field was specified, use
// that. Otherwise, use the newer "projects" field.
func getProjectsFromApplicationQuery(q application.ApplicationQuery) []string {
if q.Project != nil {
return q.Project
}
return q.Projects
}

View File

@@ -27,6 +27,8 @@ message ApplicationQuery {
optional string selector = 5;
// the repoURL to restrict returned list applications
optional string repo = 6;
// the project names to restrict returned list applications (legacy name for backwards-compatibility)
repeated string project = 8;
}
message NodeQuery {

View File

@@ -4,11 +4,13 @@ import (
"context"
coreerrors "errors"
"fmt"
"strconv"
"sync/atomic"
"testing"
"time"
synccommon "github.com/argoproj/gitops-engine/pkg/sync/common"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/argoproj/gitops-engine/pkg/utils/kube/kubetest"
"github.com/argoproj/pkg/sync"
"github.com/ghodss/yaml"
@@ -17,13 +19,17 @@ import (
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
k8sappsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/rest"
kubetesting "k8s.io/client-go/testing"
k8scache "k8s.io/client-go/tools/cache"
"k8s.io/utils/pointer"
@@ -35,10 +41,13 @@ import (
appinformer "github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions"
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
"github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks"
appmocks "github.com/argoproj/argo-cd/v2/server/application/mocks"
servercache "github.com/argoproj/argo-cd/v2/server/cache"
"github.com/argoproj/argo-cd/v2/server/rbacpolicy"
"github.com/argoproj/argo-cd/v2/test"
"github.com/argoproj/argo-cd/v2/util/assets"
"github.com/argoproj/argo-cd/v2/util/cache"
"github.com/argoproj/argo-cd/v2/util/cache/appstate"
"github.com/argoproj/argo-cd/v2/util/db"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/grpc"
@@ -93,6 +102,7 @@ func fakeRepoServerClient(isHelm bool) *mocks.RepoServerServiceClient {
mockRepoServiceClient.On("GenerateManifest", mock.Anything, mock.Anything).Return(&apiclient.ManifestResponse{}, nil)
mockRepoServiceClient.On("GetAppDetails", mock.Anything, mock.Anything).Return(&apiclient.RepoAppDetailsResponse{}, nil)
mockRepoServiceClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil)
mockRepoServiceClient.On("GetRevisionMetadata", mock.Anything, mock.Anything).Return(&appsv1.RevisionMetadata{}, nil)
if isHelm {
mockRepoServiceClient.On("ResolveRevision", mock.Anything, mock.Anything).Return(fakeResolveRevesionResponseHelm(), nil)
@@ -104,15 +114,15 @@ func fakeRepoServerClient(isHelm bool) *mocks.RepoServerServiceClient {
}
// return an ApplicationServiceServer which returns fake data
func newTestAppServer(objects ...runtime.Object) *Server {
func newTestAppServer(t *testing.T, objects ...runtime.Object) *Server {
f := func(enf *rbac.Enforcer) {
_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
enf.SetDefaultRole("role:admin")
}
return newTestAppServerWithEnforcerConfigure(f, objects...)
return newTestAppServerWithEnforcerConfigure(f, t, objects...)
}
func newTestAppServerWithEnforcerConfigure(f func(*rbac.Enforcer), objects ...runtime.Object) *Server {
func newTestAppServerWithEnforcerConfigure(f func(*rbac.Enforcer), t *testing.T, objects ...runtime.Object) *Server {
kubeclientset := fake.NewSimpleClientset(&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: testNamespace,
@@ -197,15 +207,83 @@ func newTestAppServerWithEnforcerConfigure(f func(*rbac.Enforcer), objects ...ru
panic("Timed out waiting for caches to sync")
}
broadcaster := new(appmocks.Broadcaster)
broadcaster.On("Subscribe", mock.Anything, mock.Anything).Return(func() {}).Run(func(args mock.Arguments) {
// Simulate the broadcaster notifying the subscriber of an application update.
// The second parameter to Subscribe is filters. For the purposes of tests, we ignore the filters. Future tests
// might require implementing those.
go func() {
events := args.Get(0).(chan *appsv1.ApplicationWatchEvent)
for _, obj := range objects {
app, ok := obj.(*appsv1.Application)
if ok {
oldVersion, err := strconv.Atoi(app.ResourceVersion)
if err != nil {
oldVersion = 0
}
clonedApp := app.DeepCopy()
clonedApp.ResourceVersion = fmt.Sprintf("%d", oldVersion+1)
events <- &appsv1.ApplicationWatchEvent{Type: watch.Added, Application: *clonedApp}
}
}
}()
})
broadcaster.On("OnAdd", mock.Anything).Return()
broadcaster.On("OnUpdate", mock.Anything, mock.Anything).Return()
broadcaster.On("OnDelete", mock.Anything).Return()
appStateCache := appstate.NewCache(cache.NewCache(cache.NewInMemoryCache(time.Hour)), time.Hour)
// pre-populate the app cache
for _, obj := range objects {
app, ok := obj.(*appsv1.Application)
if ok {
err := appStateCache.SetAppManagedResources(app.Name, []*appsv1.ResourceDiff{})
require.NoError(t, err)
// Pre-populate the resource tree based on the app's resources.
nodes := make([]appsv1.ResourceNode, len(app.Status.Resources))
for i, res := range app.Status.Resources {
nodes[i] = appsv1.ResourceNode{
ResourceRef: appsv1.ResourceRef{
Group: res.Group,
Kind: res.Kind,
Version: res.Version,
Name: res.Name,
Namespace: res.Namespace,
UID: "fake",
},
}
}
err = appStateCache.SetAppResourcesTree(app.Name, &appsv1.ApplicationTree{
Nodes: nodes,
})
require.NoError(t, err)
}
}
appCache := servercache.NewCache(appStateCache, time.Hour, time.Hour, time.Hour)
kubectl := &kubetest.MockKubectlCmd{}
kubectl = kubectl.WithGetResourceFunc(func(_ context.Context, _ *rest.Config, gvk schema.GroupVersionKind, name string, namespace string) (*unstructured.Unstructured, error) {
for _, obj := range objects {
if obj.GetObjectKind().GroupVersionKind().GroupKind() == gvk.GroupKind() {
if obj, ok := obj.(*unstructured.Unstructured); ok && obj.GetName() == name && obj.GetNamespace() == namespace {
return obj, nil
}
}
}
return nil, nil
})
server, _ := NewServer(
testNamespace,
kubeclientset,
fakeAppsClientset,
factory.Argoproj().V1alpha1().Applications().Lister().Applications(testNamespace),
appInformer,
broadcaster,
mockRepoClient,
nil,
&kubetest.MockKubectlCmd{},
appCache,
kubectl,
db,
enforcer,
sync.NewKeyLock(),
@@ -295,8 +373,462 @@ func createTestApp(testApp string, opts ...func(app *appsv1.Application)) *appsv
return &app
}
type TestResourceTreeServer struct {
ctx context.Context
}
func (t *TestResourceTreeServer) Send(tree *appsv1.ApplicationTree) error {
return nil
}
func (t *TestResourceTreeServer) SetHeader(metadata.MD) error {
return nil
}
func (t *TestResourceTreeServer) SendHeader(metadata.MD) error {
return nil
}
func (t *TestResourceTreeServer) SetTrailer(metadata.MD) {}
func (t *TestResourceTreeServer) Context() context.Context {
return t.ctx
}
func (t *TestResourceTreeServer) SendMsg(m interface{}) error {
return nil
}
func (t *TestResourceTreeServer) RecvMsg(m interface{}) error {
return nil
}
type TestPodLogsServer struct {
ctx context.Context
}
func (t *TestPodLogsServer) Send(log *application.LogEntry) error {
return nil
}
func (t *TestPodLogsServer) SetHeader(metadata.MD) error {
return nil
}
func (t *TestPodLogsServer) SendHeader(metadata.MD) error {
return nil
}
func (t *TestPodLogsServer) SetTrailer(metadata.MD) {}
func (t *TestPodLogsServer) Context() context.Context {
return t.ctx
}
func (t *TestPodLogsServer) SendMsg(m interface{}) error {
return nil
}
func (t *TestPodLogsServer) RecvMsg(m interface{}) error {
return nil
}
func TestNoAppEnumeration(t *testing.T) {
// This test ensures that malicious users can't infer the existence or non-existence of Applications by inspecting
// error messages. The errors for "app does not exist" must be the same as errors for "you aren't allowed to
// interact with this app."
// These tests are only important on API calls where the full app RBAC name (project, namespace, and name) is _not_
// known based on the query parameters. For example, the Create call cannot leak existence of Applications, because
// the Application's project, namespace, and name are all specified in the API call. The call can be rejected
// immediately if the user does not have access. But the Delete endpoint may be called with just the Application
// name. So we cannot return a different error message for "does not exist" and "you don't have delete permissions,"
// because the user could infer that the Application exists if they do not get the "does not exist" message. For
// endpoints that do not require the full RBAC name, we must return a generic "permission denied" for both "does not
// exist" and "no access."
f := func(enf *rbac.Enforcer) {
_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
enf.SetDefaultRole("role:none")
}
deployment := k8sappsv1.Deployment{
TypeMeta: metav1.TypeMeta{
APIVersion: "apps/v1",
Kind: "Deployment",
},
ObjectMeta: metav1.ObjectMeta{
Name: "test",
Namespace: "test",
},
}
testApp := newTestApp(func(app *appsv1.Application) {
app.Name = "test"
app.Status.Resources = []appsv1.ResourceStatus{
{
Group: deployment.GroupVersionKind().Group,
Kind: deployment.GroupVersionKind().Kind,
Version: deployment.GroupVersionKind().Version,
Name: deployment.Name,
Namespace: deployment.Namespace,
Status: "Synced",
},
}
app.Status.History = []appsv1.RevisionHistory{
{
ID: 0,
Source: appsv1.ApplicationSource{
TargetRevision: "something-old",
},
},
}
})
testDeployment := kube.MustToUnstructured(&deployment)
appServer := newTestAppServerWithEnforcerConfigure(f, t, testApp, testDeployment)
noRoleCtx := context.Background()
// nolint:staticcheck
adminCtx := context.WithValue(noRoleCtx, "claims", &jwt.MapClaims{"groups": []string{"admin"}})
t.Run("Get", func(t *testing.T) {
_, err := appServer.Get(adminCtx, &application.ApplicationQuery{Name: pointer.String("test")})
assert.NoError(t, err)
_, err = appServer.Get(noRoleCtx, &application.ApplicationQuery{Name: pointer.String("test")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.Get(adminCtx, &application.ApplicationQuery{Name: pointer.String("doest-not-exist")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
})
t.Run("GetManifests", func(t *testing.T) {
_, err := appServer.GetManifests(adminCtx, &application.ApplicationManifestQuery{Name: pointer.String("test")})
assert.NoError(t, err)
_, err = appServer.GetManifests(noRoleCtx, &application.ApplicationManifestQuery{Name: pointer.String("test")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.GetManifests(adminCtx, &application.ApplicationManifestQuery{Name: pointer.String("doest-not-exist")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
})
t.Run("ListResourceEvents", func(t *testing.T) {
_, err := appServer.ListResourceEvents(adminCtx, &application.ApplicationResourceEventsQuery{Name: pointer.String("test")})
assert.NoError(t, err)
_, err = appServer.ListResourceEvents(noRoleCtx, &application.ApplicationResourceEventsQuery{Name: pointer.String("test")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.ListResourceEvents(adminCtx, &application.ApplicationResourceEventsQuery{Name: pointer.String("doest-not-exist")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
})
t.Run("UpdateSpec", func(t *testing.T) {
_, err := appServer.UpdateSpec(adminCtx, &application.ApplicationUpdateSpecRequest{Name: pointer.String("test"), Spec: &appsv1.ApplicationSpec{
Destination: appsv1.ApplicationDestination{Namespace: "default", Server: "https://cluster-api.com"},
Source: appsv1.ApplicationSource{RepoURL: "https://some-fake-source", Path: "."},
}})
assert.NoError(t, err)
_, err = appServer.UpdateSpec(noRoleCtx, &application.ApplicationUpdateSpecRequest{Name: pointer.String("test"), Spec: &appsv1.ApplicationSpec{
Destination: appsv1.ApplicationDestination{Namespace: "default", Server: "https://cluster-api.com"},
Source: appsv1.ApplicationSource{RepoURL: "https://some-fake-source", Path: "."},
}})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.UpdateSpec(adminCtx, &application.ApplicationUpdateSpecRequest{Name: pointer.String("doest-not-exist"), Spec: &appsv1.ApplicationSpec{
Destination: appsv1.ApplicationDestination{Namespace: "default", Server: "https://cluster-api.com"},
Source: appsv1.ApplicationSource{RepoURL: "https://some-fake-source", Path: "."},
}})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
})
t.Run("Patch", func(t *testing.T) {
_, err := appServer.Patch(adminCtx, &application.ApplicationPatchRequest{Name: pointer.String("test"), Patch: pointer.String(`[{"op": "replace", "path": "/spec/source/path", "value": "foo"}]`)})
assert.NoError(t, err)
_, err = appServer.Patch(noRoleCtx, &application.ApplicationPatchRequest{Name: pointer.String("test"), Patch: pointer.String(`[{"op": "replace", "path": "/spec/source/path", "value": "foo"}]`)})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.Patch(adminCtx, &application.ApplicationPatchRequest{Name: pointer.String("doest-not-exist")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
})
t.Run("GetResource", func(t *testing.T) {
_, err := appServer.GetResource(adminCtx, &application.ApplicationResourceRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")})
assert.NoError(t, err)
_, err = appServer.GetResource(noRoleCtx, &application.ApplicationResourceRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.GetResource(adminCtx, &application.ApplicationResourceRequest{Name: pointer.String("doest-not-exist"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
})
t.Run("PatchResource", func(t *testing.T) {
_, err := appServer.PatchResource(adminCtx, &application.ApplicationResourcePatchRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test"), Patch: pointer.String(`[{"op": "replace", "path": "/spec/replicas", "value": 3}]`)})
// This will always throw an error, because the kubectl mock for PatchResource is hard-coded to return nil.
// The best we can do is to confirm we get past the permission check.
assert.NotEqual(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.PatchResource(noRoleCtx, &application.ApplicationResourcePatchRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test"), Patch: pointer.String(`[{"op": "replace", "path": "/spec/replicas", "value": 3}]`)})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.PatchResource(adminCtx, &application.ApplicationResourcePatchRequest{Name: pointer.String("doest-not-exist"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test"), Patch: pointer.String(`[{"op": "replace", "path": "/spec/replicas", "value": 3}]`)})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
})
t.Run("DeleteResource", func(t *testing.T) {
_, err := appServer.DeleteResource(adminCtx, &application.ApplicationResourceDeleteRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")})
assert.NoError(t, err)
_, err = appServer.DeleteResource(noRoleCtx, &application.ApplicationResourceDeleteRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.DeleteResource(adminCtx, &application.ApplicationResourceDeleteRequest{Name: pointer.String("doest-not-exist"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
})
t.Run("ResourceTree", func(t *testing.T) {
_, err := appServer.ResourceTree(adminCtx, &application.ResourcesQuery{ApplicationName: pointer.String("test")})
assert.NoError(t, err)
_, err = appServer.ResourceTree(noRoleCtx, &application.ResourcesQuery{ApplicationName: pointer.String("test")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.ResourceTree(adminCtx, &application.ResourcesQuery{ApplicationName: pointer.String("doest-not-exist")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
})
t.Run("RevisionMetadata", func(t *testing.T) {
_, err := appServer.RevisionMetadata(adminCtx, &application.RevisionMetadataQuery{Name: pointer.String("test")})
assert.NoError(t, err)
_, err = appServer.RevisionMetadata(noRoleCtx, &application.RevisionMetadataQuery{Name: pointer.String("test")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.RevisionMetadata(adminCtx, &application.RevisionMetadataQuery{Name: pointer.String("doest-not-exist")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
})
t.Run("ManagedResources", func(t *testing.T) {
_, err := appServer.ManagedResources(adminCtx, &application.ResourcesQuery{ApplicationName: pointer.String("test")})
assert.NoError(t, err)
_, err = appServer.ManagedResources(noRoleCtx, &application.ResourcesQuery{ApplicationName: pointer.String("test")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.ManagedResources(adminCtx, &application.ResourcesQuery{ApplicationName: pointer.String("doest-not-exist")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
})
t.Run("Sync", func(t *testing.T) {
_, err := appServer.Sync(adminCtx, &application.ApplicationSyncRequest{Name: pointer.String("test")})
assert.NoError(t, err)
_, err = appServer.Sync(noRoleCtx, &application.ApplicationSyncRequest{Name: pointer.String("test")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.Sync(adminCtx, &application.ApplicationSyncRequest{Name: pointer.String("doest-not-exist")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
})
t.Run("TerminateOperation", func(t *testing.T) {
// The sync operation is already started from the previous test. We just need to set the field that the
// controller would set if this were an actual Argo CD environment.
setSyncRunningOperationState(t, appServer)
_, err := appServer.TerminateOperation(adminCtx, &application.OperationTerminateRequest{Name: pointer.String("test")})
assert.NoError(t, err)
_, err = appServer.TerminateOperation(noRoleCtx, &application.OperationTerminateRequest{Name: pointer.String("test")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.TerminateOperation(adminCtx, &application.OperationTerminateRequest{Name: pointer.String("doest-not-exist")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
})
t.Run("Rollback", func(t *testing.T) {
unsetSyncRunningOperationState(t, appServer)
_, err := appServer.Rollback(adminCtx, &application.ApplicationRollbackRequest{Name: pointer.String("test")})
assert.NoError(t, err)
_, err = appServer.Rollback(noRoleCtx, &application.ApplicationRollbackRequest{Name: pointer.String("test")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.Rollback(adminCtx, &application.ApplicationRollbackRequest{Name: pointer.String("doest-not-exist")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
})
t.Run("ListResourceActions", func(t *testing.T) {
_, err := appServer.ListResourceActions(adminCtx, &application.ApplicationResourceRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")})
assert.NoError(t, err)
_, err = appServer.ListResourceActions(noRoleCtx, &application.ApplicationResourceRequest{Name: pointer.String("test")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.ListResourceActions(noRoleCtx, &application.ApplicationResourceRequest{Group: pointer.String("argoproj.io"), Kind: pointer.String("Application"), Name: pointer.String("test")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.ListResourceActions(adminCtx, &application.ApplicationResourceRequest{Name: pointer.String("doest-not-exist")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
})
t.Run("RunResourceAction", func(t *testing.T) {
_, err := appServer.RunResourceAction(adminCtx, &application.ResourceActionRunRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test"), Action: pointer.String("restart")})
assert.NoError(t, err)
_, err = appServer.RunResourceAction(noRoleCtx, &application.ResourceActionRunRequest{Name: pointer.String("test")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.RunResourceAction(noRoleCtx, &application.ResourceActionRunRequest{Group: pointer.String("argoproj.io"), Kind: pointer.String("Application"), Name: pointer.String("test")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.RunResourceAction(adminCtx, &application.ResourceActionRunRequest{Name: pointer.String("doest-not-exist")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
})
t.Run("GetApplicationSyncWindows", func(t *testing.T) {
_, err := appServer.GetApplicationSyncWindows(adminCtx, &application.ApplicationSyncWindowsQuery{Name: pointer.String("test")})
assert.NoError(t, err)
_, err = appServer.GetApplicationSyncWindows(noRoleCtx, &application.ApplicationSyncWindowsQuery{Name: pointer.String("test")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.GetApplicationSyncWindows(adminCtx, &application.ApplicationSyncWindowsQuery{Name: pointer.String("doest-not-exist")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
})
t.Run("WatchResourceTree", func(t *testing.T) {
err := appServer.WatchResourceTree(&application.ResourcesQuery{ApplicationName: pointer.String("test")}, &TestResourceTreeServer{ctx: adminCtx})
assert.NoError(t, err)
err = appServer.WatchResourceTree(&application.ResourcesQuery{ApplicationName: pointer.String("test")}, &TestResourceTreeServer{ctx: noRoleCtx})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
err = appServer.WatchResourceTree(&application.ResourcesQuery{ApplicationName: pointer.String("does-not-exist")}, &TestResourceTreeServer{ctx: adminCtx})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
})
t.Run("PodLogs", func(t *testing.T) {
err := appServer.PodLogs(&application.ApplicationPodLogsQuery{Name: pointer.String("test")}, &TestPodLogsServer{ctx: adminCtx})
assert.NoError(t, err)
err = appServer.PodLogs(&application.ApplicationPodLogsQuery{Name: pointer.String("test")}, &TestPodLogsServer{ctx: noRoleCtx})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
err = appServer.PodLogs(&application.ApplicationPodLogsQuery{Name: pointer.String("does-not-exist")}, &TestPodLogsServer{ctx: adminCtx})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
})
// Do this last so other stuff doesn't fail.
t.Run("Delete", func(t *testing.T) {
_, err := appServer.Delete(adminCtx, &application.ApplicationDeleteRequest{Name: pointer.String("test")})
assert.NoError(t, err)
_, err = appServer.Delete(noRoleCtx, &application.ApplicationDeleteRequest{Name: pointer.String("test")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
_, err = appServer.Delete(adminCtx, &application.ApplicationDeleteRequest{Name: pointer.String("doest-not-exist")})
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
})
}
// setSyncRunningOperationState simulates starting a sync operation on the given app.
func setSyncRunningOperationState(t *testing.T, appServer *Server) {
appIf := appServer.appclientset.ArgoprojV1alpha1().Applications("default")
app, err := appIf.Get(context.Background(), "test", metav1.GetOptions{})
require.NoError(t, err)
// This sets the status that would be set by the controller usually.
app.Status.OperationState = &appsv1.OperationState{Phase: synccommon.OperationRunning, Operation: appsv1.Operation{Sync: &appsv1.SyncOperation{}}}
_, err = appIf.Update(context.Background(), app, metav1.UpdateOptions{})
require.NoError(t, err)
}
// unsetSyncRunningOperationState simulates finishing a sync operation on the given app.
func unsetSyncRunningOperationState(t *testing.T, appServer *Server) {
appIf := appServer.appclientset.ArgoprojV1alpha1().Applications("default")
app, err := appIf.Get(context.Background(), "test", metav1.GetOptions{})
require.NoError(t, err)
app.Operation = nil
app.Status.OperationState = nil
_, err = appIf.Update(context.Background(), app, metav1.UpdateOptions{})
require.NoError(t, err)
}
func testListAppsWithLabels(t *testing.T, appQuery application.ApplicationQuery, appServer *Server) {
validTests := []struct {
testName string
label string
expectedResult []string
}{
{testName: "Equality based filtering using '=' operator",
label: "key1=value1",
expectedResult: []string{"App1"}},
{testName: "Equality based filtering using '==' operator",
label: "key1==value1",
expectedResult: []string{"App1"}},
{testName: "Equality based filtering using '!=' operator",
label: "key1!=value1",
expectedResult: []string{"App2", "App3"}},
{testName: "Set based filtering using 'in' operator",
label: "key1 in (value1, value3)",
expectedResult: []string{"App1", "App3"}},
{testName: "Set based filtering using 'notin' operator",
label: "key1 notin (value1, value3)",
expectedResult: []string{"App2"}},
{testName: "Set based filtering using 'exists' operator",
label: "key1",
expectedResult: []string{"App1", "App2", "App3"}},
{testName: "Set based filtering using 'not exists' operator",
label: "!key2",
expectedResult: []string{"App2", "App3"}},
}
//test valid scenarios
for _, validTest := range validTests {
t.Run(validTest.testName, func(t *testing.T) {
appQuery.Selector = &validTest.label
res, err := appServer.List(context.Background(), &appQuery)
assert.NoError(t, err)
apps := []string{}
for i := range res.Items {
apps = append(apps, res.Items[i].Name)
}
assert.Equal(t, validTest.expectedResult, apps)
})
}
invalidTests := []struct {
testName string
label string
errorMesage string
}{
{testName: "Set based filtering using '>' operator",
label: "key1>value1",
errorMesage: "error parsing the selector"},
{testName: "Set based filtering using '<' operator",
label: "key1<value1",
errorMesage: "error parsing the selector"},
}
//test invalid scenarios
for _, invalidTest := range invalidTests {
t.Run(invalidTest.testName, func(t *testing.T) {
appQuery.Selector = &invalidTest.label
_, err := appServer.List(context.Background(), &appQuery)
assert.ErrorContains(t, err, invalidTest.errorMesage)
})
}
}
func TestListAppWithProjects(t *testing.T) {
appServer := newTestAppServer(t, newTestApp(func(app *appsv1.Application) {
app.Name = "App1"
app.Spec.Project = "test-project1"
}), newTestApp(func(app *appsv1.Application) {
app.Name = "App2"
app.Spec.Project = "test-project2"
}), newTestApp(func(app *appsv1.Application) {
app.Name = "App3"
app.Spec.Project = "test-project3"
}))
t.Run("List all apps", func(t *testing.T) {
appQuery := application.ApplicationQuery{}
appList, err := appServer.List(context.Background(), &appQuery)
assert.NoError(t, err)
assert.Len(t, appList.Items, 3)
})
t.Run("List apps with projects filter set", func(t *testing.T) {
appQuery := application.ApplicationQuery{Projects: []string{"test-project1"}}
appList, err := appServer.List(context.Background(), &appQuery)
assert.NoError(t, err)
assert.Len(t, appList.Items, 1)
for _, app := range appList.Items {
assert.Equal(t, "test-project1", app.Spec.Project)
}
})
t.Run("List apps with project filter set (legacy field)", func(t *testing.T) {
appQuery := application.ApplicationQuery{Project: []string{"test-project1"}}
appList, err := appServer.List(context.Background(), &appQuery)
assert.NoError(t, err)
assert.Len(t, appList.Items, 1)
for _, app := range appList.Items {
assert.Equal(t, "test-project1", app.Spec.Project)
}
})
t.Run("List apps with both projects and project filter set", func(t *testing.T) {
// If the older field is present, we should use it instead of the newer field.
appQuery := application.ApplicationQuery{Project: []string{"test-project1"}, Projects: []string{"test-project2"}}
appList, err := appServer.List(context.Background(), &appQuery)
assert.NoError(t, err)
assert.Len(t, appList.Items, 1)
for _, app := range appList.Items {
assert.Equal(t, "test-project1", app.Spec.Project)
}
})
}
func TestListApps(t *testing.T) {
appServer := newTestAppServer(newTestApp(func(app *appsv1.Application) {
appServer := newTestAppServer(t, newTestApp(func(app *appsv1.Application) {
app.Name = "bcd"
}), newTestApp(func(app *appsv1.Application) {
app.Name = "abc"
@@ -344,7 +876,7 @@ g, group-49, role:test3
`
_ = enf.SetUserPolicy(policy)
}
appServer := newTestAppServerWithEnforcerConfigure(f, objects...)
appServer := newTestAppServerWithEnforcerConfigure(f, t, objects...)
res, err := appServer.List(ctx, &application.ApplicationQuery{})
@@ -358,7 +890,7 @@ g, group-49, role:test3
func TestCreateApp(t *testing.T) {
testApp := newTestApp()
appServer := newTestAppServer()
appServer := newTestAppServer(t)
testApp.Spec.Project = ""
createReq := application.ApplicationCreateRequest{
Application: testApp,
@@ -371,7 +903,7 @@ func TestCreateApp(t *testing.T) {
}
func TestCreateAppWithDestName(t *testing.T) {
appServer := newTestAppServer()
appServer := newTestAppServer(t)
testApp := newTestAppWithDestName()
createReq := application.ApplicationCreateRequest{
Application: testApp,
@@ -384,7 +916,7 @@ func TestCreateAppWithDestName(t *testing.T) {
func TestUpdateApp(t *testing.T) {
testApp := newTestApp()
appServer := newTestAppServer(testApp)
appServer := newTestAppServer(t, testApp)
testApp.Spec.Project = ""
app, err := appServer.Update(context.Background(), &application.ApplicationUpdateRequest{
Application: testApp,
@@ -395,7 +927,7 @@ func TestUpdateApp(t *testing.T) {
func TestUpdateAppSpec(t *testing.T) {
testApp := newTestApp()
appServer := newTestAppServer(testApp)
appServer := newTestAppServer(t, testApp)
testApp.Spec.Project = ""
spec, err := appServer.UpdateSpec(context.Background(), &application.ApplicationUpdateSpecRequest{
Name: &testApp.Name,
@@ -410,7 +942,7 @@ func TestUpdateAppSpec(t *testing.T) {
func TestDeleteApp(t *testing.T) {
ctx := context.Background()
appServer := newTestAppServer()
appServer := newTestAppServer(t)
createReq := application.ApplicationCreateRequest{
Application: newTestApp(),
}
@@ -495,20 +1027,9 @@ func TestDeleteApp(t *testing.T) {
})
}
func TestDeleteApp_InvalidName(t *testing.T) {
appServer := newTestAppServer()
_, err := appServer.Delete(context.Background(), &application.ApplicationDeleteRequest{
Name: pointer.StringPtr("foo"),
})
if !assert.Error(t, err) {
return
}
assert.True(t, apierrors.IsNotFound(err))
}
func TestSyncAndTerminate(t *testing.T) {
ctx := context.Background()
appServer := newTestAppServer()
appServer := newTestAppServer(t)
testApp := newTestApp()
testApp.Spec.Source.RepoURL = "https://github.com/argoproj/argo-cd.git"
createReq := application.ApplicationCreateRequest{
@@ -548,7 +1069,7 @@ func TestSyncAndTerminate(t *testing.T) {
func TestSyncHelm(t *testing.T) {
ctx := context.Background()
appServer := newTestAppServer()
appServer := newTestAppServer(t)
testApp := newTestApp()
testApp.Spec.Source.RepoURL = "https://argoproj.github.io/argo-helm"
testApp.Spec.Source.Path = ""
@@ -572,7 +1093,7 @@ func TestSyncHelm(t *testing.T) {
func TestSyncGit(t *testing.T) {
ctx := context.Background()
appServer := newTestAppServer()
appServer := newTestAppServer(t)
testApp := newTestApp()
testApp.Spec.Source.RepoURL = "https://github.com/org/test"
testApp.Spec.Source.Path = "deploy"
@@ -605,7 +1126,7 @@ func TestRollbackApp(t *testing.T) {
Revision: "abc",
Source: *testApp.Spec.Source.DeepCopy(),
}}
appServer := newTestAppServer(testApp)
appServer := newTestAppServer(t, testApp)
updatedApp, err := appServer.Rollback(context.Background(), &application.ApplicationRollbackRequest{
Name: &testApp.Name,
@@ -625,56 +1146,63 @@ func TestUpdateAppProject(t *testing.T) {
ctx := context.Background()
// nolint:staticcheck
ctx = context.WithValue(ctx, "claims", &jwt.StandardClaims{Subject: "admin"})
appServer := newTestAppServer(testApp)
appServer := newTestAppServer(t, testApp)
appServer.enf.SetDefaultRole("")
// Verify normal update works (without changing project)
_ = appServer.enf.SetBuiltinPolicy(`p, admin, applications, update, default/test-app, allow`)
_, err := appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
assert.NoError(t, err)
t.Run("update without changing project", func(t *testing.T) {
_ = appServer.enf.SetBuiltinPolicy(`p, admin, applications, update, default/test-app, allow`)
_, err := appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
assert.NoError(t, err)
})
// Verify caller cannot update to another project
testApp.Spec.Project = "my-proj"
_, err = appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
assert.Equal(t, status.Code(err), codes.PermissionDenied)
t.Run("cannot update to another project", func(t *testing.T) {
testApp.Spec.Project = "my-proj"
_, err := appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
assert.Equal(t, status.Code(err), codes.PermissionDenied)
})
// Verify inability to change projects without create privileges in new project
_ = appServer.enf.SetBuiltinPolicy(`
t.Run("cannot change projects without create privileges", func(t *testing.T) {
_ = appServer.enf.SetBuiltinPolicy(`
p, admin, applications, update, default/test-app, allow
p, admin, applications, update, my-proj/test-app, allow
`)
_, err = appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
statusErr := grpc.UnwrapGRPCStatus(err)
assert.NotNil(t, statusErr)
assert.Equal(t, codes.PermissionDenied, statusErr.Code())
_, err := appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
statusErr := grpc.UnwrapGRPCStatus(err)
assert.NotNil(t, statusErr)
assert.Equal(t, codes.PermissionDenied, statusErr.Code())
})
// Verify inability to change projects without update privileges in new project
_ = appServer.enf.SetBuiltinPolicy(`
t.Run("cannot change projects without update privileges in new project", func(t *testing.T) {
_ = appServer.enf.SetBuiltinPolicy(`
p, admin, applications, update, default/test-app, allow
p, admin, applications, create, my-proj/test-app, allow
`)
_, err = appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
assert.Equal(t, status.Code(err), codes.PermissionDenied)
_, err := appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
assert.Equal(t, status.Code(err), codes.PermissionDenied)
})
// Verify inability to change projects without update privileges in old project
_ = appServer.enf.SetBuiltinPolicy(`
t.Run("cannot change projects without update privileges in old project", func(t *testing.T) {
_ = appServer.enf.SetBuiltinPolicy(`
p, admin, applications, create, my-proj/test-app, allow
p, admin, applications, update, my-proj/test-app, allow
`)
_, err = appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
statusErr = grpc.UnwrapGRPCStatus(err)
assert.NotNil(t, statusErr)
assert.Equal(t, codes.PermissionDenied, statusErr.Code())
_, err := appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
statusErr := grpc.UnwrapGRPCStatus(err)
assert.NotNil(t, statusErr)
assert.Equal(t, codes.PermissionDenied, statusErr.Code())
})
// Verify can update project with proper permissions
_ = appServer.enf.SetBuiltinPolicy(`
t.Run("can update project with proper permissions", func(t *testing.T) {
// Verify can update project with proper permissions
_ = appServer.enf.SetBuiltinPolicy(`
p, admin, applications, update, default/test-app, allow
p, admin, applications, create, my-proj/test-app, allow
p, admin, applications, update, my-proj/test-app, allow
`)
updatedApp, err := appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
assert.NoError(t, err)
assert.Equal(t, "my-proj", updatedApp.Spec.Project)
updatedApp, err := appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
assert.NoError(t, err)
assert.Equal(t, "my-proj", updatedApp.Spec.Project)
})
}
func TestAppJsonPatch(t *testing.T) {
@@ -682,7 +1210,7 @@ func TestAppJsonPatch(t *testing.T) {
ctx := context.Background()
// nolint:staticcheck
ctx = context.WithValue(ctx, "claims", &jwt.StandardClaims{Subject: "admin"})
appServer := newTestAppServer(testApp)
appServer := newTestAppServer(t, testApp)
appServer.enf.SetDefaultRole("")
app, err := appServer.Patch(ctx, &application.ApplicationPatchRequest{Name: &testApp.Name, Patch: pointer.String("garbage")})
@@ -707,7 +1235,7 @@ func TestAppMergePatch(t *testing.T) {
ctx := context.Background()
// nolint:staticcheck
ctx = context.WithValue(ctx, "claims", &jwt.StandardClaims{Subject: "admin"})
appServer := newTestAppServer(testApp)
appServer := newTestAppServer(t, testApp)
appServer.enf.SetDefaultRole("")
app, err := appServer.Patch(ctx, &application.ApplicationPatchRequest{
@@ -720,7 +1248,7 @@ func TestServer_GetApplicationSyncWindowsState(t *testing.T) {
t.Run("Active", func(t *testing.T) {
testApp := newTestApp()
testApp.Spec.Project = "proj-maint"
appServer := newTestAppServer(testApp)
appServer := newTestAppServer(t, testApp)
active, err := appServer.GetApplicationSyncWindows(context.Background(), &application.ApplicationSyncWindowsQuery{Name: &testApp.Name})
assert.NoError(t, err)
@@ -729,7 +1257,7 @@ func TestServer_GetApplicationSyncWindowsState(t *testing.T) {
t.Run("Inactive", func(t *testing.T) {
testApp := newTestApp()
testApp.Spec.Project = "default"
appServer := newTestAppServer(testApp)
appServer := newTestAppServer(t, testApp)
active, err := appServer.GetApplicationSyncWindows(context.Background(), &application.ApplicationSyncWindowsQuery{Name: &testApp.Name})
assert.NoError(t, err)
@@ -738,7 +1266,7 @@ func TestServer_GetApplicationSyncWindowsState(t *testing.T) {
t.Run("ProjectDoesNotExist", func(t *testing.T) {
testApp := newTestApp()
testApp.Spec.Project = "none"
appServer := newTestAppServer(testApp)
appServer := newTestAppServer(t, testApp)
active, err := appServer.GetApplicationSyncWindows(context.Background(), &application.ApplicationSyncWindowsQuery{Name: &testApp.Name})
assert.Contains(t, err.Error(), "not found")
@@ -749,8 +1277,14 @@ func TestServer_GetApplicationSyncWindowsState(t *testing.T) {
func TestGetCachedAppState(t *testing.T) {
testApp := newTestApp()
testApp.ObjectMeta.ResourceVersion = "1"
testApp.Spec.Project = "none"
appServer := newTestAppServer(testApp)
testApp.Spec.Project = "test-proj"
testProj := &appsv1.AppProject{
ObjectMeta: metav1.ObjectMeta{
Name: "test-proj",
Namespace: testNamespace,
},
}
appServer := newTestAppServer(t, testApp, testProj)
fakeClientSet := appServer.appclientset.(*apps.Clientset)
t.Run("NoError", func(t *testing.T) {
err := appServer.getCachedAppState(context.Background(), testApp, func() error {
@@ -922,7 +1456,7 @@ func TestGetAppRefresh_NormalRefresh(t *testing.T) {
defer cancel()
testApp := newTestApp()
testApp.ObjectMeta.ResourceVersion = "1"
appServer := newTestAppServer(testApp)
appServer := newTestAppServer(t, testApp)
var patched int32
@@ -950,7 +1484,7 @@ func TestGetAppRefresh_HardRefresh(t *testing.T) {
defer cancel()
testApp := newTestApp()
testApp.ObjectMeta.ResourceVersion = "1"
appServer := newTestAppServer(testApp)
appServer := newTestAppServer(t, testApp)
var getAppDetailsQuery *apiclient.RepoServerAppDetailsQuery
mockRepoServiceClient := mocks.RepoServerServiceClient{}

View File

@@ -23,6 +23,14 @@ func (s *subscriber) matches(event *appv1.ApplicationWatchEvent) bool {
return true
}
// Broadcaster is an interface for broadcasting application informer watch events to multiple subscribers.
type Broadcaster interface {
Subscribe(ch chan *appv1.ApplicationWatchEvent, filters ...func(event *appv1.ApplicationWatchEvent) bool) func()
OnAdd(interface{})
OnUpdate(interface{}, interface{})
OnDelete(interface{})
}
type broadcasterHandler struct {
lock sync.Mutex
subscribers []*subscriber

View File

@@ -0,0 +1,66 @@
// Code generated by mockery v2.13.1. DO NOT EDIT.
package mocks
import (
v1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
mock "github.com/stretchr/testify/mock"
)
// Broadcaster is an autogenerated mock type for the Broadcaster type
type Broadcaster struct {
mock.Mock
}
// OnAdd provides a mock function with given fields: _a0
func (_m *Broadcaster) OnAdd(_a0 interface{}) {
_m.Called(_a0)
}
// OnDelete provides a mock function with given fields: _a0
func (_m *Broadcaster) OnDelete(_a0 interface{}) {
_m.Called(_a0)
}
// OnUpdate provides a mock function with given fields: _a0, _a1
func (_m *Broadcaster) OnUpdate(_a0 interface{}, _a1 interface{}) {
_m.Called(_a0, _a1)
}
// Subscribe provides a mock function with given fields: ch, filters
func (_m *Broadcaster) Subscribe(ch chan *v1alpha1.ApplicationWatchEvent, filters ...func(*v1alpha1.ApplicationWatchEvent) bool) func() {
_va := make([]interface{}, len(filters))
for _i := range filters {
_va[_i] = filters[_i]
}
var _ca []interface{}
_ca = append(_ca, ch)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 func()
if rf, ok := ret.Get(0).(func(chan *v1alpha1.ApplicationWatchEvent, ...func(*v1alpha1.ApplicationWatchEvent) bool) func()); ok {
r0 = rf(ch, filters...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(func())
}
}
return r0
}
type mockConstructorTestingTNewBroadcaster interface {
mock.TestingT
Cleanup(func())
}
// NewBroadcaster creates a new instance of Broadcaster. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewBroadcaster(t mockConstructorTestingTNewBroadcaster) *Broadcaster {
mock := &Broadcaster{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@@ -25,6 +25,7 @@ import (
"github.com/argoproj/argo-cd/v2/util/db"
"github.com/argoproj/argo-cd/v2/util/rbac"
sessionmgr "github.com/argoproj/argo-cd/v2/util/session"
"github.com/argoproj/argo-cd/v2/util/settings"
)
type terminalHandler struct {
@@ -91,6 +92,26 @@ func isValidContainerName(name string) bool {
return len(validationErrors) == 0
}
type GetSettingsFunc func() (*settings.ArgoCDSettings, error)
// WithFeatureFlagMiddleware is an HTTP middleware to verify if the terminal
// feature is enabled before invoking the main handler
func (s *terminalHandler) WithFeatureFlagMiddleware(getSettings GetSettingsFunc) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
argocdSettings, err := getSettings()
if err != nil {
log.Errorf("error executing WithFeatureFlagMiddleware: error getting settings: %s", err)
http.Error(w, "Failed to get settings", http.StatusBadRequest)
return
}
if !argocdSettings.ExecEnabled {
w.WriteHeader(http.StatusNotFound)
return
}
s.ServeHTTP(w, r)
})
}
func (s *terminalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query()

View File

@@ -13,6 +13,7 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/kubernetes"
"github.com/argoproj/argo-cd/v2/common"
"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"
@@ -134,7 +135,7 @@ func (s *Server) Get(ctx context.Context, q *cluster.ClusterQuery) (*appv1.Clust
func (s *Server) getClusterWith403IfNotExist(ctx context.Context, q *cluster.ClusterQuery) (*appv1.Cluster, error) {
repo, err := s.getCluster(ctx, q)
if err != nil || repo == nil {
return nil, status.Error(codes.PermissionDenied, "permission denied")
return nil, common.PermissionDeniedAPIError
}
return repo, nil
}
@@ -220,14 +221,14 @@ func (s *Server) Update(ctx context.Context, q *cluster.ClusterUpdateRequest) (*
}
// verify that user can do update inside project where cluster is located
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, createRBACObject(c.Project, q.Cluster.Server)); err != nil {
return nil, err
if !s.enf.Enforce(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, createRBACObject(c.Project, c.Server)) {
return nil, common.PermissionDeniedAPIError
}
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 !s.enf.Enforce(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, createRBACObject(q.Cluster.Project, c.Server)) {
return nil, common.PermissionDeniedAPIError
}
}

View File

@@ -2,6 +2,7 @@ package cluster
import (
"context"
"fmt"
"testing"
"time"
@@ -48,6 +49,117 @@ func newNoopEnforcer() *rbac.Enforcer {
return enf
}
func TestUpdateCluster_RejectInvalidParams(t *testing.T) {
testCases := []struct {
name string
request clusterapi.ClusterUpdateRequest
}{
{
name: "allowed cluster URL in body, disallowed cluster URL in query",
request: clusterapi.ClusterUpdateRequest{Cluster: &v1alpha1.Cluster{Name: "", Server: "https://127.0.0.1", Project: "", ClusterResources: true}, Id: &clusterapi.ClusterID{Type: "", Value: "https://127.0.0.2"}, UpdatedFields: []string{"clusterResources", "project"}},
},
{
name: "allowed cluster URL in body, disallowed cluster name in query",
request: clusterapi.ClusterUpdateRequest{Cluster: &v1alpha1.Cluster{Name: "", Server: "https://127.0.0.1", Project: "", ClusterResources: true}, Id: &clusterapi.ClusterID{Type: "name", Value: "disallowed-unscoped"}, UpdatedFields: []string{"clusterResources", "project"}},
},
{
name: "allowed cluster URL in body, disallowed cluster name in query, changing unscoped to scoped",
request: clusterapi.ClusterUpdateRequest{Cluster: &v1alpha1.Cluster{Name: "", Server: "https://127.0.0.1", Project: "allowed-project", ClusterResources: true}, Id: &clusterapi.ClusterID{Type: "", Value: "https://127.0.0.2"}, UpdatedFields: []string{"clusterResources", "project"}},
},
{
name: "allowed cluster URL in body, disallowed cluster URL in query, changing unscoped to scoped",
request: clusterapi.ClusterUpdateRequest{Cluster: &v1alpha1.Cluster{Name: "", Server: "https://127.0.0.1", Project: "allowed-project", ClusterResources: true}, Id: &clusterapi.ClusterID{Type: "name", Value: "disallowed-unscoped"}, UpdatedFields: []string{"clusterResources", "project"}},
},
}
db := &dbmocks.ArgoDB{}
clusters := []v1alpha1.Cluster{
{
Name: "allowed-unscoped",
Server: "https://127.0.0.1",
},
{
Name: "disallowed-unscoped",
Server: "https://127.0.0.2",
},
{
Name: "allowed-scoped",
Server: "https://127.0.0.3",
Project: "allowed-project",
},
{
Name: "disallowed-scoped",
Server: "https://127.0.0.4",
Project: "disallowed-project",
},
}
db.On("ListClusters", mock.Anything).Return(
func(ctx context.Context) *v1alpha1.ClusterList {
return &v1alpha1.ClusterList{
ListMeta: v1.ListMeta{},
Items: clusters,
}
},
func(ctx context.Context) error {
return nil
},
)
db.On("UpdateCluster", mock.Anything, mock.Anything).Return(
func(ctx context.Context, c *v1alpha1.Cluster) *v1alpha1.Cluster {
for _, cluster := range clusters {
if c.Server == cluster.Server {
return c
}
}
return nil
},
func(ctx context.Context, c *v1alpha1.Cluster) error {
for _, cluster := range clusters {
if c.Server == cluster.Server {
return nil
}
}
return fmt.Errorf("cluster '%s' not found", c.Server)
},
)
db.On("GetCluster", mock.Anything, mock.Anything).Return(
func(ctx context.Context, server string) *v1alpha1.Cluster {
for _, cluster := range clusters {
if server == cluster.Server {
return &cluster
}
}
return nil
},
func(ctx context.Context, server string) error {
for _, cluster := range clusters {
if server == cluster.Server {
return nil
}
}
return fmt.Errorf("cluster '%s' not found", server)
},
)
enf := rbac.NewEnforcer(fake.NewSimpleClientset(test.NewFakeConfigMap()), test.FakeArgoCDNamespace, common.ArgoCDConfigMapName, nil)
_ = enf.SetBuiltinPolicy(`p, role:test, clusters, *, https://127.0.0.1, allow
p, role:test, clusters, *, allowed-project/*, allow`)
enf.SetDefaultRole("role:test")
server := NewServer(db, enf, newServerInMemoryCache(), &kubetest.MockKubectlCmd{})
for _, c := range testCases {
cc := c
t.Run(cc.name, func(t *testing.T) {
t.Parallel()
out, err := server.Update(context.Background(), &cc.request)
require.Nil(t, out)
assert.ErrorIs(t, err, common.PermissionDeniedAPIError)
})
}
}
func TestGetCluster_UrlEncodedName(t *testing.T) {
db := &dbmocks.ArgoDB{}

View File

@@ -2,6 +2,7 @@ package server
import (
"context"
netCtx "context"
"crypto/tls"
"fmt"
goio "io"
@@ -24,8 +25,6 @@ import (
// nolint:staticcheck
golang_proto "github.com/golang/protobuf/proto"
netCtx "context"
"github.com/argoproj/pkg/sync"
"github.com/go-redis/redis/v8"
"github.com/golang-jwt/jwt/v4"
@@ -55,6 +54,8 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"github.com/pkg/errors"
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/pkg/apiclient"
accountpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/account"
@@ -112,7 +113,6 @@ import (
"github.com/argoproj/argo-cd/v2/util/swagger"
tlsutil "github.com/argoproj/argo-cd/v2/util/tls"
"github.com/argoproj/argo-cd/v2/util/webhook"
"github.com/pkg/errors"
)
const maxConcurrentLoginRequestsCountEnv = "ARGOCD_MAX_CONCURRENT_LOGIN_REQUESTS_COUNT"
@@ -423,8 +423,9 @@ func (a *ArgoCDServer) Run(ctx context.Context, listeners *Listeners) {
// If not matched, we assume that its TLS.
tlsl := tcpm.Match(cmux.Any())
tlsConfig := tls.Config{
Certificates: []tls.Certificate{*a.settings.Certificate},
tlsConfig := tls.Config{}
tlsConfig.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
return a.settings.Certificate, nil
}
if a.TLSConfigCustomizer != nil {
a.TLSConfigCustomizer(&tlsConfig)
@@ -566,8 +567,8 @@ func (a *ArgoCDServer) watchSettings() {
newCert, newCertKey = tlsutil.EncodeX509KeyPairString(*a.settings.Certificate)
}
if newCert != prevCert || newCertKey != prevCertKey {
log.Infof("tls certificate modified. restarting")
break
log.Infof("tls certificate modified. reloading certificate")
// No need to break out of this loop since TlsConfig.GetCertificate will automagically reload the cert.
}
}
}
@@ -680,6 +681,7 @@ func (a *ArgoCDServer) newGRPCServer() (*grpc.Server, application.AppResourceTre
a.AppClientset,
a.appLister,
a.appInformer,
nil,
a.RepoClientset,
a.Cache,
kubectl,
@@ -812,40 +814,10 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl
}
mux.Handle("/api/", handler)
terminalHandler := application.NewHandler(a.appLister, a.db, a.enf, a.Cache, appResourceTreeFn, a.settings.ExecShells)
mux.HandleFunc("/terminal", func(writer http.ResponseWriter, request *http.Request) {
argocdSettings, err := a.settingsMgr.GetSettings()
if err != nil {
http.Error(writer, fmt.Sprintf("Failed to get settings: %v", err), http.StatusBadRequest)
return
}
if !argocdSettings.ExecEnabled {
writer.WriteHeader(http.StatusNotFound)
return
}
if !a.DisableAuth {
ctx := request.Context()
cookies := request.Cookies()
tokenString, err := httputil.JoinCookies(common.AuthCookieName, cookies)
if err == nil && jwtutil.IsValid(tokenString) {
claims, _, err := a.sessionMgr.VerifyToken(tokenString)
if err != nil {
// nolint:staticcheck
ctx = context.WithValue(ctx, util_session.AuthErrorCtxKey, err)
} else if claims != nil {
// Add claims to the context to inspect for RBAC
// nolint:staticcheck
ctx = context.WithValue(ctx, "claims", claims)
}
request = request.WithContext(ctx)
} else {
writer.WriteHeader(http.StatusUnauthorized)
return
}
}
terminalHandler.ServeHTTP(writer, request)
})
terminal := application.NewHandler(a.appLister, a.db, a.enf, a.Cache, appResourceTreeFn, a.settings.ExecShells).
WithFeatureFlagMiddleware(a.settingsMgr.GetSettings)
th := util_session.WithAuthMiddleware(a.DisableAuth, a.sessionMgr, terminal)
mux.Handle("/terminal", th)
mustRegisterGWHandler(versionpkg.RegisterVersionServiceHandler, ctx, gwmux, conn)
mustRegisterGWHandler(clusterpkg.RegisterClusterServiceHandler, ctx, gwmux, conn)

View File

@@ -33,6 +33,7 @@ import (
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
"github.com/argoproj/argo-cd/v2/util/rbac"
settings_util "github.com/argoproj/argo-cd/v2/util/settings"
testutil "github.com/argoproj/argo-cd/v2/util/test"
)
func fakeServer() (*ArgoCDServer, func()) {
@@ -508,7 +509,7 @@ func dexMockHandler(t *testing.T, url string) func(http.ResponseWriter, *http.Re
}
}
func getTestServer(t *testing.T, anonymousEnabled bool, withFakeSSO bool) (argocd *ArgoCDServer, dexURL string) {
func getTestServer(t *testing.T, anonymousEnabled bool, withFakeSSO bool, useDexForSSO bool) (argocd *ArgoCDServer, oidcURL string) {
cm := test.NewFakeConfigMap()
if anonymousEnabled {
cm.Data["users.anonymous.enabled"] = "true"
@@ -519,9 +520,14 @@ func getTestServer(t *testing.T, anonymousEnabled bool, withFakeSSO bool) (argoc
ts.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
dexMockHandler(t, ts.URL)(w, r)
})
oidcServer := ts
if !useDexForSSO {
oidcServer = testutil.GetOIDCTestServer(t)
}
if withFakeSSO {
cm.Data["url"] = ts.URL
cm.Data["dex.config"] = `
if useDexForSSO {
cm.Data["dex.config"] = `
connectors:
# OIDC
- type: OIDC
@@ -531,6 +537,19 @@ connectors:
issuer: https://auth.example.gom
clientID: test-client
clientSecret: $dex.oidc.clientSecret`
} else {
oidcConfig := settings_util.OIDCConfig{
Name: "Okta",
Issuer: oidcServer.URL,
ClientID: "argo-cd",
ClientSecret: "$oidc.okta.clientSecret",
}
oidcConfigString, err := yaml.Marshal(oidcConfig)
require.NoError(t, err)
cm.Data["oidc.config"] = string(oidcConfigString)
// Avoid bothering with certs for local tests.
cm.Data["oidc.tls.insecure.skip.verify"] = "true"
}
}
secret := test.NewFakeSecret()
kubeclientset := fake.NewSimpleClientset(cm, secret)
@@ -540,27 +559,32 @@ connectors:
KubeClientset: kubeclientset,
AppClientset: appClientSet,
}
if withFakeSSO {
if withFakeSSO && useDexForSSO {
argoCDOpts.DexServerAddr = ts.URL
}
argocd = NewServer(context.Background(), argoCDOpts)
return argocd, ts.URL
return argocd, oidcServer.URL
}
func TestAuthenticate_3rd_party_JWTs(t *testing.T) {
// Marshaling single strings to strings is typical, so we test for this relatively common behavior.
jwt.MarshalSingleStringAsArray = false
type testData struct {
test string
anonymousEnabled bool
claims jwt.RegisteredClaims
expectedErrorContains string
expectedClaims interface{}
useDex bool
}
var tests = []testData{
// Dex
{
test: "anonymous disabled, no audience",
anonymousEnabled: false,
claims: jwt.RegisteredClaims{},
expectedErrorContains: "no audience found in the token",
claims: jwt.RegisteredClaims{ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))},
expectedErrorContains: common.TokenVerificationError,
expectedClaims: nil,
},
{
@@ -573,31 +597,95 @@ func TestAuthenticate_3rd_party_JWTs(t *testing.T) {
{
test: "anonymous disabled, unexpired token, admin claim",
anonymousEnabled: false,
claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))},
expectedErrorContains: "id token signed with unsupported algorithm",
claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{common.ArgoCDClientAppID}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))},
expectedErrorContains: common.TokenVerificationError,
expectedClaims: nil,
},
{
test: "anonymous enabled, unexpired token, admin claim",
anonymousEnabled: true,
claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))},
claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{common.ArgoCDClientAppID}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))},
expectedErrorContains: "",
expectedClaims: "",
},
{
test: "anonymous disabled, expired token, admin claim",
anonymousEnabled: false,
claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now())},
expectedErrorContains: "token is expired",
claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{common.ArgoCDClientAppID}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now())},
expectedErrorContains: common.TokenVerificationError,
expectedClaims: jwt.RegisteredClaims{Issuer: "sso"},
},
{
test: "anonymous enabled, expired token, admin claim",
anonymousEnabled: true,
claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now())},
claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{common.ArgoCDClientAppID}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now())},
expectedErrorContains: "",
expectedClaims: "",
},
{
test: "anonymous disabled, unexpired token, admin claim, incorrect audience",
anonymousEnabled: false,
claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"incorrect-audience"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))},
expectedErrorContains: common.TokenVerificationError,
expectedClaims: nil,
},
// External OIDC (not bundled Dex)
{
test: "external OIDC: anonymous disabled, no audience",
anonymousEnabled: false,
claims: jwt.RegisteredClaims{ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))},
useDex: true,
expectedErrorContains: common.TokenVerificationError,
expectedClaims: nil,
},
{
test: "external OIDC: anonymous enabled, no audience",
anonymousEnabled: true,
claims: jwt.RegisteredClaims{},
useDex: true,
expectedErrorContains: "",
expectedClaims: "",
},
{
test: "external OIDC: anonymous disabled, unexpired token, admin claim",
anonymousEnabled: false,
claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{common.ArgoCDClientAppID}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))},
useDex: true,
expectedErrorContains: common.TokenVerificationError,
expectedClaims: nil,
},
{
test: "external OIDC: anonymous enabled, unexpired token, admin claim",
anonymousEnabled: true,
claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{common.ArgoCDClientAppID}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))},
useDex: true,
expectedErrorContains: "",
expectedClaims: "",
},
{
test: "external OIDC: anonymous disabled, expired token, admin claim",
anonymousEnabled: false,
claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{common.ArgoCDClientAppID}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now())},
useDex: true,
expectedErrorContains: common.TokenVerificationError,
expectedClaims: jwt.RegisteredClaims{Issuer: "sso"},
},
{
test: "external OIDC: anonymous enabled, expired token, admin claim",
anonymousEnabled: true,
claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{common.ArgoCDClientAppID}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now())},
useDex: true,
expectedErrorContains: "",
expectedClaims: "",
},
{
test: "external OIDC: anonymous disabled, unexpired token, admin claim, incorrect audience",
anonymousEnabled: false,
claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"incorrect-audience"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))},
useDex: true,
expectedErrorContains: common.TokenVerificationError,
expectedClaims: nil,
},
}
for _, testData := range tests {
@@ -609,8 +697,13 @@ func TestAuthenticate_3rd_party_JWTs(t *testing.T) {
// Must be declared here to avoid race.
ctx := context.Background() //nolint:ineffassign,staticcheck
argocd, dexURL := getTestServer(t, testDataCopy.anonymousEnabled, true)
testDataCopy.claims.Issuer = fmt.Sprintf("%s/api/dex", dexURL)
argocd, oidcURL := getTestServer(t, testDataCopy.anonymousEnabled, true, testDataCopy.useDex)
if testDataCopy.useDex {
testDataCopy.claims.Issuer = fmt.Sprintf("%s/api/dex", oidcURL)
} else {
testDataCopy.claims.Issuer = oidcURL
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, testDataCopy.claims)
tokenString, err := token.SignedString([]byte("key"))
require.NoError(t, err)
@@ -660,7 +753,7 @@ func TestAuthenticate_no_request_metadata(t *testing.T) {
t.Run(testDataCopy.test, func(t *testing.T) {
t.Parallel()
argocd, _ := getTestServer(t, testDataCopy.anonymousEnabled, true)
argocd, _ := getTestServer(t, testDataCopy.anonymousEnabled, true, true)
ctx := context.Background()
ctx, err := argocd.Authenticate(ctx)
@@ -706,7 +799,7 @@ func TestAuthenticate_no_SSO(t *testing.T) {
// Must be declared here to avoid race.
ctx := context.Background() //nolint:ineffassign,staticcheck
argocd, dexURL := getTestServer(t, testDataCopy.anonymousEnabled, false)
argocd, dexURL := getTestServer(t, testDataCopy.anonymousEnabled, false, true)
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{Issuer: fmt.Sprintf("%s/api/dex", dexURL)})
tokenString, err := token.SignedString([]byte("key"))
require.NoError(t, err)
@@ -779,7 +872,7 @@ func TestAuthenticate_bad_request_metadata(t *testing.T) {
test: "anonymous disabled, bad auth header",
anonymousEnabled: false,
metadata: metadata.MD{"authorization": []string{"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiJ9.TGGTTHuuGpEU8WgobXxkrBtW3NiR3dgw5LR-1DEW3BQ"}},
expectedErrorMessage: "no audience found in the token",
expectedErrorMessage: common.TokenVerificationError,
expectedClaims: nil,
},
{
@@ -793,7 +886,7 @@ func TestAuthenticate_bad_request_metadata(t *testing.T) {
test: "anonymous disabled, bad auth cookie",
anonymousEnabled: false,
metadata: metadata.MD{"grpcgateway-cookie": []string{"argocd.token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiJ9.TGGTTHuuGpEU8WgobXxkrBtW3NiR3dgw5LR-1DEW3BQ"}},
expectedErrorMessage: "no audience found in the token",
expectedErrorMessage: common.TokenVerificationError,
expectedClaims: nil,
},
{
@@ -814,7 +907,7 @@ func TestAuthenticate_bad_request_metadata(t *testing.T) {
// Must be declared here to avoid race.
ctx := context.Background() //nolint:ineffassign,staticcheck
argocd, _ := getTestServer(t, testDataCopy.anonymousEnabled, true)
argocd, _ := getTestServer(t, testDataCopy.anonymousEnabled, true, true)
ctx = metadata.NewIncomingContext(context.Background(), testDataCopy.metadata)
ctx, err := argocd.Authenticate(ctx)

View File

@@ -1,4 +1,4 @@
FROM redis:7.0.4 as redis
FROM docker.io/library/redis:7.0.8-alpine as redis
FROM node:12.18.4-buster as node

View File

@@ -1,6 +1,6 @@
controller: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && 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 $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
api-server: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_BINARY_NAME=argocd-server $COMMAND --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 "test $ARGOCD_IN_CI = true && exit 0; ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/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"
controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && 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 $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
api-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_BINARY_NAME=argocd-server $COMMAND --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 "test $ARGOCD_IN_CI = true && exit 0; ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/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.36.0 serve /dex.yaml"
redis: sh -c "/usr/local/bin/redis-server --save "" --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}"
repo-server: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_BINARY_NAME=argocd-repo-server $COMMAND --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379}"
ui: sh -c "test $ARGOCD_IN_CI = true && exit 0; cd ui && ARGOCD_E2E_YARN_HOST=0.0.0.0 ${ARGOCD_E2E_YARN_CMD:-yarn} start"

View File

@@ -416,7 +416,9 @@ func TestInvalidAppProject(t *testing.T) {
IgnoreErrors().
CreateApp().
Then().
Expect(Error("", "application references project does-not-exist which does not exist"))
// We're not allowed to infer whether the project exists based on this error message. Instead, we get a generic
// permission denied error.
Expect(Error("", "permission denied"))
}
func TestAppDeletion(t *testing.T) {

View File

@@ -782,6 +782,8 @@ func RestartRepoServer() {
}
FailOnErr(Run("", "kubectl", "rollout", "restart", "deployment", workload))
FailOnErr(Run("", "kubectl", "rollout", "status", "deployment", workload))
// wait longer to avoid error on s390x
time.Sleep(10 * time.Second)
}
}

View File

@@ -10,6 +10,7 @@
"test": "jest"
},
"dependencies": {
"@types/superagent": "^4.1.15",
"ansi-to-react": "^6.1.6",
"argo-ui": "git+https://github.com/argoproj/argo-ui.git",
"buffer": "^6.0.3",
@@ -42,8 +43,7 @@
"react-svg-piechart": "^2.4.2",
"redoc": "^2.0.0-rc.64",
"rxjs": "^6.6.6",
"superagent": "^3.8.2",
"superagent-promise": "^1.1.0",
"superagent": "^7.1.3",
"timezones-list": "3.0.1",
"unidiff": "^1.0.2",
"url": "^0.11.0",
@@ -79,7 +79,6 @@
"@types/react-router": "^4.0.27",
"@types/react-router-dom": "^4.2.3",
"@types/react-test-renderer": "^16.8.3",
"@types/superagent": "^3.5.7",
"add": "^2.0.6",
"babel-jest": "^24.9.0",
"babel-loader": "^8.0.6",

View File

@@ -48,7 +48,9 @@ export const EventsList = (props: {events: models.Event[]}) => {
<div className={`argo-table-list__row events-list__event events-list__event--${event.type}`} key={event.metadata.uid}>
<div className='row'>
<div className='columns small-2 xxlarge-2'>{event.reason}</div>
<div className='columns small-4 xxlarge-5'>{event.message}</div>
<div className='columns small-4 xxlarge-5' style={{whiteSpace: 'normal'}}>
{event.message}
</div>
<div className='columns small-2 xxlarge-1'>{event.count}</div>
<div className='columns small-2 xxlarge-2'>{event.firstTimestamp ? getTimeElements(event.firstTimestamp) : getTimeElements(event.eventTime)}</div>
<div className='columns small-2 xxlarge-2'>{event.lastTimestamp ? getTimeElements(event.lastTimestamp) : getTimeElements(event.eventTime)}</div>

View File

@@ -1,6 +1,6 @@
import * as path from 'path';
import * as superagent from 'superagent';
const superagentPromise = require('superagent-promise');
import * as agent from 'superagent';
import {BehaviorSubject, Observable, Observer} from 'rxjs';
import {filter} from 'rxjs/operators';
@@ -22,11 +22,9 @@ enum ReadyState {
DONE = 4
}
const agent: superagent.SuperAgentStatic = superagentPromise(superagent, global.Promise);
let baseHRef = '/';
const onError = new BehaviorSubject<superagent.ResponseError>(null);
const onError = new BehaviorSubject<agent.ResponseError>(null);
function toAbsURL(val: string): string {
return path.join(baseHRef, val);
@@ -36,7 +34,7 @@ function apiRoot(): string {
return toAbsURL('/api/v1');
}
function initHandlers(req: superagent.Request) {
function initHandlers(req: agent.Request) {
req.on('error', err => onError.next(err));
return req;
}

View File

@@ -1,6 +1,8 @@
import {Tooltip} from 'argo-ui';
import * as React from 'react';
import {combineLatest} from 'rxjs';
import {map} from 'rxjs/operators';
import {ExternalLink} from '../applications/components/application-urls';
import {DataLoader} from '../shared/components';
import {services, ViewPreferences} from '../shared/services';
@@ -66,6 +68,14 @@ export const Banner = (props: React.Props<any>) => {
chatBottomPosition = 85;
}
}
if (chatUrl) {
try {
const externalLink = new ExternalLink(chatUrl);
chatUrl = externalLink.ref;
} catch (InvalidExternalLinkError) {
chatUrl = 'invalid-url';
}
}
return (
<React.Fragment>
<div className={combinedBannerClassName} style={{visibility: show ? 'visible' : 'hidden', height: heightOfBanner}}>
@@ -96,9 +106,17 @@ export const Banner = (props: React.Props<any>) => {
{show ? <div className={wrapperClassname}>{props.children}</div> : props.children}
{chatUrl && (
<div style={{position: 'fixed', right: 10, bottom: chatBottomPosition}}>
<a href={chatUrl} className='argo-button argo-button--special'>
<i className='fas fa-comment-alt' /> {chatText}
</a>
{chatUrl === 'invalid-url' ? (
<Tooltip content='Invalid URL provided'>
<a className='argo-button disabled argo-button--special'>
<i className='fas fa-comment-alt' /> {chatText}
</a>
</Tooltip>
) : (
<a href={chatUrl} className='argo-button argo-button--special'>
<i className='fas fa-comment-alt' /> {chatText}
</a>
)}
</div>
)}
</React.Fragment>

View File

@@ -1885,10 +1885,10 @@
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
"@types/superagent@^3.5.7":
version "3.8.7"
resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-3.8.7.tgz#1f1ed44634d5459b3a672eb7235a8e7cfd97704c"
integrity sha512-9KhCkyXv268A2nZ1Wvu7rQWM+BmdYUVkycFeNnYrUL5Zwu7o8wPQ3wBfW59dDP+wuoxw0ww8YKgTNv8j/cgscA==
"@types/superagent@^4.1.15":
version "4.1.15"
resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.15.tgz#63297de457eba5e2bc502a7609426c4cceab434a"
integrity sha512-mu/N4uvfDN2zVQQ5AYJI/g4qxn2bHB6521t1UuH09ShNWjebTqN0ZFuYK9uYjcgmI0dTQEs+Owi1EO6U0OkOZQ==
dependencies:
"@types/cookiejar" "*"
"@types/node" "*"
@@ -2407,6 +2407,11 @@ array-unique@^0.3.2:
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
asap@^2.0.0:
version "2.0.6"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
assign-symbols@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
@@ -3062,7 +3067,7 @@ colorette@^2.0.10, colorette@^2.0.14:
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da"
integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==
combined-stream@^1.0.6, combined-stream@^1.0.8:
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
@@ -3099,7 +3104,7 @@ commondir@^1.0.1:
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
component-emitter@^1.2.0, component-emitter@^1.2.1:
component-emitter@^1.2.1, component-emitter@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
@@ -3168,10 +3173,10 @@ cookie@0.4.0:
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
cookiejar@^2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c"
integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==
cookiejar@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.3.tgz#fc7a6216e408e74414b90230050842dacda75acc"
integrity sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==
copy-descriptor@^0.1.0:
version "0.1.1"
@@ -3373,13 +3378,20 @@ debug@4, debug@^4.1.0, debug@^4.1.1:
dependencies:
ms "^2.1.1"
debug@^3.1.0, debug@^3.1.1:
debug@^3.1.1:
version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
dependencies:
ms "^2.1.1"
debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
decimal.js@^10.2.1:
version "10.3.1"
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783"
@@ -3512,6 +3524,14 @@ detect-node@^2.0.4:
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
dezalgo@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81"
integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==
dependencies:
asap "^2.0.0"
wrappy "1"
diff-match-patch@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.5.tgz#abb584d5f10cd1196dfc55aa03701592ae3f7b37"
@@ -4109,11 +4129,6 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2:
assign-symbols "^1.0.0"
is-extendable "^1.0.1"
extend@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
extglob@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
@@ -4164,6 +4179,11 @@ fast-safe-stringify@^2.0.7:
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.8.tgz#dc2af48c46cf712b683e849b2bbd446b32de936f"
integrity sha512-lXatBjf3WPjmWD6DpIZxkeSsCOwqI0maYMpgDlx8g4U2qi4lbjA9oH/HD2a87G+KfsUmo5WbJFmqBZlPxtptag==
fast-safe-stringify@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884"
integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==
fastest-levenshtein@^1.0.12:
version "1.0.12"
resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2"
@@ -4283,15 +4303,6 @@ foreach@^2.0.4:
resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k=
form-data@^2.3.1:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.6"
mime-types "^2.1.12"
form-data@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f"
@@ -4301,10 +4312,24 @@ form-data@^3.0.0:
combined-stream "^1.0.8"
mime-types "^2.1.12"
formidable@^1.2.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.2.tgz#bf69aea2972982675f00865342b982986f6b8dd9"
integrity sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
formidable@^2.0.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/formidable/-/formidable-2.1.1.tgz#81269cbea1a613240049f5f61a9d97731517414f"
integrity sha512-0EcS9wCFEzLvfiks7omJ+SiYJAiD+TzK4Pcw1UlUoGnhUxDcMKjt0P7x8wEb0u6OHu8Nb98WG3nxtlF5C7bvUQ==
dependencies:
dezalgo "^1.0.4"
hexoid "^1.0.0"
once "^1.4.0"
qs "^6.11.0"
forwarded@0.2.0:
version "0.2.0"
@@ -4582,6 +4607,11 @@ he@^1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
hexoid@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18"
integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==
history@^4.10.1, history@^4.7.2:
version "4.10.1"
resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3"
@@ -6158,7 +6188,7 @@ merge2@^1.3.0:
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
methods@^1.1.1, methods@~1.1.2:
methods@^1.1.2, methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
@@ -6222,11 +6252,16 @@ mime-types@^2.1.27, mime-types@^2.1.31:
dependencies:
mime-db "1.52.0"
mime@1.6.0, mime@^1.4.1:
mime@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
mime@^2.5.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367"
integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==
mimic-fn@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
@@ -6365,7 +6400,7 @@ ms@2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
ms@^2.1.1:
ms@2.1.2, ms@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
@@ -7204,10 +7239,17 @@ qs@6.7.0:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
qs@^6.5.1, qs@^6.9.4:
version "6.10.1"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a"
integrity sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==
qs@^6.10.3:
version "6.10.3"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e"
integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==
dependencies:
side-channel "^1.0.4"
qs@^6.11.0, qs@^6.9.4:
version "6.11.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
dependencies:
side-channel "^1.0.4"
@@ -7943,7 +7985,7 @@ read-pkg@^3.0.0:
normalize-package-data "^2.3.2"
path-type "^3.0.0"
readable-stream@^2.0.1, readable-stream@^2.3.5:
readable-stream@^2.0.1:
version "2.3.6"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
@@ -7956,7 +7998,7 @@ readable-stream@^2.0.1, readable-stream@^2.3.5:
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
readable-stream@^3.0.6:
readable-stream@^3.0.6, readable-stream@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
@@ -8420,6 +8462,13 @@ semver@^6.0.0, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.3.7:
version "7.3.7"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
dependencies:
lru-cache "^6.0.0"
send@0.17.1:
version "0.17.1"
resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
@@ -8998,26 +9047,22 @@ style-loader@^3.3.1:
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.1.tgz#057dfa6b3d4d7c7064462830f9113ed417d38575"
integrity sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==
superagent-promise@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/superagent-promise/-/superagent-promise-1.1.0.tgz#baf22d8bbdd439a9b07dd10f8c08f54fe2503533"
integrity sha1-uvIti73UOamwfdEPjAj1T+JQNTM=
superagent@^3.8.2:
version "3.8.3"
resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.3.tgz#460ea0dbdb7d5b11bc4f78deba565f86a178e128"
integrity sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==
superagent@^7.1.3:
version "7.1.3"
resolved "https://registry.yarnpkg.com/superagent/-/superagent-7.1.3.tgz#783ff8330e7c2dad6ad8f0095edc772999273b6b"
integrity sha512-WA6et4nAvgBCS73lJvv1D0ssI5uk5Gh+TGN/kNe+B608EtcVs/yzfl+OLXTzDs7tOBDIpvgh/WUs1K2OK1zTeQ==
dependencies:
component-emitter "^1.2.0"
cookiejar "^2.1.0"
debug "^3.1.0"
extend "^3.0.0"
form-data "^2.3.1"
formidable "^1.2.0"
methods "^1.1.1"
mime "^1.4.1"
qs "^6.5.1"
readable-stream "^2.3.5"
component-emitter "^1.3.0"
cookiejar "^2.1.3"
debug "^4.3.4"
fast-safe-stringify "^2.1.1"
form-data "^4.0.0"
formidable "^2.0.1"
methods "^1.1.2"
mime "^2.5.0"
qs "^6.10.3"
readable-stream "^3.6.0"
semver "^7.3.7"
supports-color@^2.0.0:
version "2.0.0"

View File

@@ -16,7 +16,7 @@ spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: k8s.gcr.io/nginx-slim:0.8
image: registry.k8s.io/nginx-slim:0.8
ports:
- containerPort: 80
name: web

View File

@@ -348,9 +348,12 @@ func (a *ClientApp) HandleCallback(w http.ResponseWriter, r *http.Request) {
http.Error(w, "no id_token in token response", http.StatusInternalServerError)
return
}
idToken, err := a.provider.Verify(a.clientID, idTokenRAW)
idToken, err := a.provider.Verify(idTokenRAW, a.settings)
if err != nil {
http.Error(w, fmt.Sprintf("invalid session token: %v", err), http.StatusInternalServerError)
log.Warnf("Failed to verify token: %s", err)
http.Error(w, common.TokenVerificationError, http.StatusInternalServerError)
return
}
path := "/"

View File

@@ -89,7 +89,7 @@ func (p *fakeProvider) ParseConfig() (*OIDCConfiguration, error) {
return nil, nil
}
func (p *fakeProvider) Verify(_, _ string) (*gooidc.IDToken, error) {
func (p *fakeProvider) Verify(_ string, _ *settings.ArgoCDSettings) (*gooidc.IDToken, error) {
return nil, nil
}

View File

@@ -2,6 +2,7 @@ package oidc
import (
"context"
"errors"
"fmt"
"net/http"
"strings"
@@ -9,8 +10,13 @@ import (
gooidc "github.com/coreos/go-oidc"
log "github.com/sirupsen/logrus"
"golang.org/x/oauth2"
"github.com/argoproj/argo-cd/v2/util/security"
"github.com/argoproj/argo-cd/v2/util/settings"
)
var ErrTokenExpired = errors.New("token is expired")
// Provider is a wrapper around go-oidc provider to also provide the following features:
// 1. lazy initialization/querying of the provider
// 2. automatic detection of change in signing keys
@@ -23,7 +29,7 @@ type Provider interface {
ParseConfig() (*OIDCConfiguration, error)
Verify(clientID, tokenString string) (*gooidc.IDToken, error)
Verify(tokenString string, argoSettings *settings.ArgoCDSettings) (*gooidc.IDToken, error)
}
type providerImpl struct {
@@ -69,13 +75,70 @@ func (p *providerImpl) newGoOIDCProvider() (*gooidc.Provider, error) {
return prov, nil
}
func (p *providerImpl) Verify(clientID, tokenString string) (*gooidc.IDToken, error) {
func (p *providerImpl) Verify(tokenString string, argoSettings *settings.ArgoCDSettings) (*gooidc.IDToken, error) {
// According to the JWT spec, the aud claim is optional. The spec also says (emphasis mine):
//
// If the principal processing the claim does not identify itself with a value in the "aud" claim _when this
// claim is present_, then the JWT MUST be rejected.
//
// - https://www.rfc-editor.org/rfc/rfc7519#section-4.1.3
//
// If the claim is not present, we can skip the audience claim check (called the "ClientID check" in go-oidc's
// terminology).
//
// The OIDC spec says that the aud claim is required (https://openid.net/specs/openid-connect-core-1_0.html#IDToken).
// But we cannot assume that all OIDC providers will follow the spec. For Argo CD <2.6.0, we will default to
// allowing the aud claim to be optional. In Argo CD >=2.6.0, we will default to requiring the aud claim to be
// present and give users the skipAudienceCheckWhenTokenHasNoAudience setting to revert the behavior if necessary.
//
// At this point, we have not verified that the token has not been altered. All code paths below MUST VERIFY
// THE TOKEN SIGNATURE to confirm that an attacker did not maliciously remove the "aud" claim.
unverifiedHasAudClaim, err := security.UnverifiedHasAudClaim(tokenString)
if err != nil {
return nil, fmt.Errorf("failed to determine whether the token has an aud claim: %w", err)
}
var idToken *gooidc.IDToken
if !unverifiedHasAudClaim {
idToken, err = p.verify("", tokenString, argoSettings.SkipAudienceCheckWhenTokenHasNoAudience())
} else {
allowedAudiences := argoSettings.OAuth2AllowedAudiences()
if len(allowedAudiences) == 0 {
return nil, errors.New("token has an audience claim, but no allowed audiences are configured")
}
// Token must be verified for at least one allowed audience
for _, aud := range allowedAudiences {
idToken, err = p.verify(aud, tokenString, false)
if err != nil && strings.HasPrefix(err.Error(), "oidc: token is expired") {
// If the token is expired, we won't bother checking other audiences. It's important to return a
// ErrTokenExpired instead of an error related to an incorrect audience, because the caller may
// have specific behavior to handle expired tokens.
break
}
if err == nil {
break
}
}
}
if err != nil {
if strings.HasPrefix(err.Error(), "oidc: token is expired") {
return nil, ErrTokenExpired
}
return nil, fmt.Errorf("failed to verify token: %w", err)
}
return idToken, nil
}
func (p *providerImpl) verify(clientID, tokenString string, skipClientIDCheck bool) (*gooidc.IDToken, error) {
ctx := context.Background()
prov, err := p.provider()
if err != nil {
return nil, err
}
verifier := prov.Verifier(&gooidc.Config{ClientID: clientID})
config := &gooidc.Config{ClientID: clientID, SkipClientIDCheck: skipClientIDCheck}
verifier := prov.Verifier(config)
idToken, err := verifier.Verify(ctx, tokenString)
if err != nil {
// HACK: if we failed token verification, it's possible the reason was because dex
@@ -93,7 +156,7 @@ func (p *providerImpl) Verify(clientID, tokenString string) (*gooidc.IDToken, er
// return original error if we fail to re-initialize OIDC
return nil, err
}
verifier = newProvider.Verifier(&gooidc.Config{ClientID: clientID})
verifier = newProvider.Verifier(config)
idToken, err = verifier.Verify(ctx, tokenString)
if err != nil {
return nil, err

80
util/security/jwt.go Normal file
View File

@@ -0,0 +1,80 @@
package security
import (
"encoding/base64"
"encoding/json"
"fmt"
"strings"
)
// parseJWT parses a jwt and returns it as json bytes.
//
// This function DOES NOT VERIFY THE TOKEN. You still have to verify the token to confirm that the token holder has not
// altered the claims.
//
// This code is copied almost verbatim from go-oidc (https://github.com/coreos/go-oidc).
func parseJWT(p string) ([]byte, error) {
parts := strings.Split(p, ".")
if len(parts) < 2 {
return nil, fmt.Errorf("malformed jwt, expected 3 parts got %d", len(parts))
}
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return nil, fmt.Errorf("malformed jwt payload: %v", err)
}
return payload, nil
}
type audience []string
// UnmarshalJSON allows us to unmarshal either a single audience or a list of audiences.
// Taken from: https://github.com/coreos/go-oidc/blob/a8ceb9a2043fca2e43518633920db746808b1138/oidc/oidc.go#L475
func (a *audience) UnmarshalJSON(b []byte) error {
var s string
if json.Unmarshal(b, &s) == nil {
*a = audience{s}
return nil
}
var auds []string
if err := json.Unmarshal(b, &auds); err != nil {
return err
}
*a = auds
return nil
}
// jwtWithOnlyAudClaim represents a jwt where only the "aud" claim is present. This struct allows us to unmarshal a jwt
// and be confident that the only information retrieved from that jwt is the "aud" claim.
type jwtWithOnlyAudClaim struct {
Aud audience `json:"aud"`
}
// getUnverifiedAudClaim gets the "aud" claim from a jwt.
//
// This function DOES NOT VERIFY THE TOKEN. You still have to verify the token to confirm that the token holder has not
// altered the "aud" claim.
//
// This code is copied almost verbatim from go-oidc (https://github.com/coreos/go-oidc).
func getUnverifiedAudClaim(rawIDToken string) ([]string, error) {
payload, err := parseJWT(rawIDToken)
if err != nil {
return nil, fmt.Errorf("malformed jwt: %v", err)
}
var token jwtWithOnlyAudClaim
if err = json.Unmarshal(payload, &token); err != nil {
return nil, fmt.Errorf("failed to unmarshal claims: %v", err)
}
return token.Aud, nil
}
// UnverifiedHasAudClaim returns whether the "aud" claim is present in the given JWT.
//
// This function DOES NOT VERIFY THE TOKEN. You still have to verify the token to confirm that the token holder has not
// altered the "aud" claim.
func UnverifiedHasAudClaim(rawIDToken string) (bool, error) {
aud, err := getUnverifiedAudClaim(rawIDToken)
if err != nil {
return false, fmt.Errorf("failed to determine whether token had an audience claim: %w", err)
}
return aud != nil, nil
}

56
util/security/jwt_test.go Normal file
View File

@@ -0,0 +1,56 @@
package security
import (
"testing"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
utiltest "github.com/argoproj/argo-cd/v2/util/test"
)
func Test_UnverifiedHasAudClaim(t *testing.T) {
tokenForAud := func(t *testing.T, aud jwt.ClaimStrings) string {
claims := jwt.RegisteredClaims{Audience: aud, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}
token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims)
key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey)
require.NoError(t, err)
tokenString, err := token.SignedString(key)
require.NoError(t, err)
return tokenString
}
testCases := []struct {
name string
aud jwt.ClaimStrings
expectedHasAud bool
}{
{
name: "no audience",
aud: jwt.ClaimStrings{},
expectedHasAud: false,
},
{
name: "one empty audience",
aud: jwt.ClaimStrings{""},
expectedHasAud: true,
},
{
name: "one non-empty audience",
aud: jwt.ClaimStrings{"test"},
expectedHasAud: true,
},
}
for _, testCase := range testCases {
testCaseCopy := testCase
t.Run(testCaseCopy.name, func(t *testing.T) {
t.Parallel()
out, err := UnverifiedHasAudClaim(tokenForAud(t, testCaseCopy.aud))
require.NoError(t, err)
assert.Equal(t, testCaseCopy.expectedHasAud, out)
})
}
}

View File

@@ -12,7 +12,6 @@ import (
"strings"
"time"
oidc "github.com/coreos/go-oidc"
"github.com/golang-jwt/jwt/v4"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
@@ -413,7 +412,7 @@ func (mgr *SessionManager) VerifyUsernamePassword(username string, password stri
// introduces random delay to protect from timing-based user enumeration attack
delayNanoseconds := verificationDelayNoiseMin.Nanoseconds() +
int64(rand.Intn(int(verificationDelayNoiseMax.Nanoseconds()-verificationDelayNoiseMin.Nanoseconds())))
// take into account amount of time spent since the request start
// take into account amount of time spent since the request start
delayNanoseconds = delayNanoseconds - time.Since(start).Nanoseconds()
if delayNanoseconds > 0 {
mgr.sleep(time.Duration(delayNanoseconds))
@@ -457,6 +456,47 @@ func (mgr *SessionManager) VerifyUsernamePassword(username string, password stri
return nil
}
// AuthMiddlewareFunc returns a function that can be used as an
// authentication middleware for HTTP requests.
func (mgr *SessionManager) AuthMiddlewareFunc(disabled bool) func(http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
return WithAuthMiddleware(disabled, mgr, h)
}
}
// TokenVerifier defines the contract to invoke token
// verification logic
type TokenVerifier interface {
VerifyToken(token string) (jwt.Claims, string, error)
}
// WithAuthMiddleware is an HTTP middleware used to ensure incoming
// requests are authenticated before invoking the target handler. If
// disabled is true, it will just invoke the next handler in the chain.
func WithAuthMiddleware(disabled bool, authn TokenVerifier, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !disabled {
cookies := r.Cookies()
tokenString, err := httputil.JoinCookies(common.AuthCookieName, cookies)
if err != nil {
http.Error(w, "Auth cookie not found", http.StatusBadRequest)
return
}
claims, _, err := authn.VerifyToken(tokenString)
if err != nil {
http.Error(w, "Invalid token", http.StatusUnauthorized)
return
}
ctx := r.Context()
// Add claims to the context to inspect for RBAC
// nolint:staticcheck
ctx = context.WithValue(ctx, "claims", claims)
r = r.WithContext(ctx)
}
next.ServeHTTP(w, r)
})
}
// VerifyToken verifies if a token is correct. Tokens can be issued either from us or by an IDP.
// We choose how to verify based on the issuer.
func (mgr *SessionManager) VerifyToken(tokenString string) (jwt.Claims, string, error) {
@@ -477,31 +517,28 @@ func (mgr *SessionManager) VerifyToken(tokenString string) (jwt.Claims, string,
return nil, "", err
}
// Token must be verified for at least one audience
// TODO(jannfis): Is this the right way? Shouldn't we know our audience and only validate for the correct one?
var idToken *oidc.IDToken
for _, aud := range claims.Audience {
idToken, err = prov.Verify(aud, tokenString)
if err == nil {
break
}
argoSettings, err := mgr.settingsMgr.GetSettings()
if err != nil {
return nil, "", fmt.Errorf("cannot access settings while verifying the token: %w", err)
}
if argoSettings == nil {
return nil, "", fmt.Errorf("settings are not available while verifying the token")
}
idToken, err := prov.Verify(tokenString, argoSettings)
// The token verification has failed. If the token has expired, we will
// return a dummy claims only containing a value for the issuer, so the
// UI can handle expired tokens appropriately.
if err != nil {
if strings.HasPrefix(err.Error(), "oidc: token is expired") {
log.Warnf("Failed to verify token: %s", err)
if errors.Is(err, oidcutil.ErrTokenExpired) {
claims = jwt.RegisteredClaims{
Issuer: "sso",
}
return claims, "", err
return claims, "", common.TokenVerificationErr
}
return nil, "", err
}
if idToken == nil {
return nil, "", fmt.Errorf("no audience found in the token")
return nil, "", common.TokenVerificationErr
}
var claims jwt.MapClaims

View File

@@ -3,8 +3,12 @@ package session
import (
"context"
"encoding/pem"
stderrors "errors"
"fmt"
"io"
"math"
"net/http"
"net/http/httptest"
"os"
"strconv"
"strings"
@@ -221,6 +225,136 @@ func TestSessionManager_ProjectToken(t *testing.T) {
})
}
type claimsMock struct {
err error
}
func (cm *claimsMock) Valid() error {
return cm.err
}
type tokenVerifierMock struct {
claims *claimsMock
err error
}
func (tm *tokenVerifierMock) VerifyToken(token string) (jwt.Claims, string, error) {
if tm.claims == nil {
return nil, "", tm.err
}
return tm.claims, "", tm.err
}
func strPointer(str string) *string {
return &str
}
func TestSessionManager_WithAuthMiddleware(t *testing.T) {
handlerFunc := func() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
t.Helper()
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/text")
_, err := w.Write([]byte("Ok"))
if err != nil {
t.Fatalf("error writing response: %s", err)
}
}
}
type testCase struct {
name string
authDisabled bool
cookieHeader bool
verifiedClaims *claimsMock
verifyTokenErr error
expectedStatusCode int
expectedResponseBody *string
}
cases := []testCase{
{
name: "will authenticate successfully",
authDisabled: false,
cookieHeader: true,
verifiedClaims: &claimsMock{},
verifyTokenErr: nil,
expectedStatusCode: 200,
expectedResponseBody: strPointer("Ok"),
},
{
name: "will be noop if auth is disabled",
authDisabled: true,
cookieHeader: false,
verifiedClaims: nil,
verifyTokenErr: nil,
expectedStatusCode: 200,
expectedResponseBody: strPointer("Ok"),
},
{
name: "will return 400 if no cookie header",
authDisabled: false,
cookieHeader: false,
verifiedClaims: &claimsMock{},
verifyTokenErr: nil,
expectedStatusCode: 400,
expectedResponseBody: nil,
},
{
name: "will return 401 verify token fails",
authDisabled: false,
cookieHeader: true,
verifiedClaims: &claimsMock{},
verifyTokenErr: stderrors.New("token error"),
expectedStatusCode: 401,
expectedResponseBody: nil,
},
{
name: "will return 200 if claims are nil",
authDisabled: false,
cookieHeader: true,
verifiedClaims: nil,
verifyTokenErr: nil,
expectedStatusCode: 200,
expectedResponseBody: strPointer("Ok"),
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
// given
mux := http.NewServeMux()
mux.HandleFunc("/", handlerFunc())
tm := &tokenVerifierMock{
claims: tc.verifiedClaims,
err: tc.verifyTokenErr,
}
ts := httptest.NewServer(WithAuthMiddleware(tc.authDisabled, tm, mux))
defer ts.Close()
req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
if err != nil {
t.Fatalf("error creating request: %s", err)
}
if tc.cookieHeader {
req.Header.Add("Cookie", "argocd.token=123456")
}
// when
resp, err := http.DefaultClient.Do(req)
// then
assert.NoError(t, err)
assert.NotNil(t, resp)
assert.Equal(t, tc.expectedStatusCode, resp.StatusCode)
if tc.expectedResponseBody != nil {
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
actual := strings.TrimSuffix(string(body), "\n")
assert.Contains(t, actual, *tc.expectedResponseBody)
}
})
}
}
var loggedOutContext = context.Background()
// nolint:staticcheck
@@ -588,9 +722,7 @@ rootCA: |
_, _, err = mgr.VerifyToken(tokenString)
require.Error(t, err)
if !strings.Contains(err.Error(), "certificate signed by unknown authority") && !strings.Contains(err.Error(), "certificate is not trusted") {
t.Fatal("did not receive expected certificate verification failure error")
}
assert.ErrorIs(t, err, common.TokenVerificationErr)
})
t.Run("OIDC provider is external, TLS is configured", func(t *testing.T) {
@@ -625,9 +757,7 @@ requestedScopes: ["oidc"]`, oidcTestServer.URL),
_, _, err = mgr.VerifyToken(tokenString)
require.Error(t, err)
if !strings.Contains(err.Error(), "certificate signed by unknown authority") && !strings.Contains(err.Error(), "certificate is not trusted") {
t.Fatal("did not receive expected certificate verification failure error")
}
assert.ErrorIs(t, err, common.TokenVerificationErr)
})
t.Run("OIDC provider is Dex, TLS is configured", func(t *testing.T) {
@@ -662,9 +792,7 @@ requestedScopes: ["oidc"]`, oidcTestServer.URL),
_, _, err = mgr.VerifyToken(tokenString)
require.Error(t, err)
if !strings.Contains(err.Error(), "certificate signed by unknown authority") && !strings.Contains(err.Error(), "certificate is not trusted") {
t.Fatal("did not receive expected certificate verification failure error")
}
assert.ErrorIs(t, err, common.TokenVerificationErr)
})
t.Run("OIDC provider is external, TLS is configured, OIDCTLSInsecureSkipVerify is true", func(t *testing.T) {
@@ -732,4 +860,295 @@ requestedScopes: ["oidc"]`, oidcTestServer.URL),
assert.NotContains(t, err.Error(), "certificate is not trusted")
assert.NotContains(t, err.Error(), "certificate signed by unknown authority")
})
t.Run("OIDC provider is external, audience is not specified", func(t *testing.T) {
config := map[string]string{
"url": "",
"oidc.config": fmt.Sprintf(`
name: Test
issuer: %s
clientID: xxx
clientSecret: yyy
requestedScopes: ["oidc"]`, oidcTestServer.URL),
"oidc.tls.insecure.skip.verify": "true", // This isn't what we're testing.
}
// This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair
// must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled.
secretConfig := map[string][]byte{
"tls.crt": utiltest.Cert,
"tls.key": utiltest.PrivateKey,
}
settingsMgr := settings.NewSettingsManager(context.Background(), getKubeClientWithConfig(config, secretConfig), "argocd")
mgr := NewSessionManager(settingsMgr, getProjLister(), "", NewUserStateStorage(nil))
mgr.verificationDelayNoiseEnabled = false
claims := jwt.RegisteredClaims{Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}
claims.Issuer = oidcTestServer.URL
token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims)
key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey)
require.NoError(t, err)
tokenString, err := token.SignedString(key)
require.NoError(t, err)
_, _, err = mgr.VerifyToken(tokenString)
require.NoError(t, err)
})
t.Run("OIDC provider is external, audience is not specified but is required", func(t *testing.T) {
config := map[string]string{
"url": "",
"oidc.config": fmt.Sprintf(`
name: Test
issuer: %s
clientID: xxx
clientSecret: yyy
requestedScopes: ["oidc"]
skipAudienceCheckWhenTokenHasNoAudience: false`, oidcTestServer.URL),
"oidc.tls.insecure.skip.verify": "true", // This isn't what we're testing.
}
// This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair
// must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled.
secretConfig := map[string][]byte{
"tls.crt": utiltest.Cert,
"tls.key": utiltest.PrivateKey,
}
settingsMgr := settings.NewSettingsManager(context.Background(), getKubeClientWithConfig(config, secretConfig), "argocd")
mgr := NewSessionManager(settingsMgr, getProjLister(), "", NewUserStateStorage(nil))
mgr.verificationDelayNoiseEnabled = false
claims := jwt.RegisteredClaims{Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}
claims.Issuer = oidcTestServer.URL
token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims)
key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey)
require.NoError(t, err)
tokenString, err := token.SignedString(key)
require.NoError(t, err)
_, _, err = mgr.VerifyToken(tokenString)
require.Error(t, err)
assert.ErrorIs(t, err, common.TokenVerificationErr)
})
t.Run("OIDC provider is external, audience is client ID, no allowed list specified", func(t *testing.T) {
config := map[string]string{
"url": "",
"oidc.config": fmt.Sprintf(`
name: Test
issuer: %s
clientID: xxx
clientSecret: yyy
requestedScopes: ["oidc"]`, oidcTestServer.URL),
"oidc.tls.insecure.skip.verify": "true", // This isn't what we're testing.
}
// This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair
// must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled.
secretConfig := map[string][]byte{
"tls.crt": utiltest.Cert,
"tls.key": utiltest.PrivateKey,
}
settingsMgr := settings.NewSettingsManager(context.Background(), getKubeClientWithConfig(config, secretConfig), "argocd")
mgr := NewSessionManager(settingsMgr, getProjLister(), "", NewUserStateStorage(nil))
mgr.verificationDelayNoiseEnabled = false
claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"xxx"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}
claims.Issuer = oidcTestServer.URL
token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims)
key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey)
require.NoError(t, err)
tokenString, err := token.SignedString(key)
require.NoError(t, err)
_, _, err = mgr.VerifyToken(tokenString)
require.NoError(t, err)
})
t.Run("OIDC provider is external, audience is in allowed list", func(t *testing.T) {
config := map[string]string{
"url": "",
"oidc.config": fmt.Sprintf(`
name: Test
issuer: %s
clientID: xxx
clientSecret: yyy
requestedScopes: ["oidc"]
allowedAudiences:
- something`, oidcTestServer.URL),
"oidc.tls.insecure.skip.verify": "true", // This isn't what we're testing.
}
// This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair
// must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled.
secretConfig := map[string][]byte{
"tls.crt": utiltest.Cert,
"tls.key": utiltest.PrivateKey,
}
settingsMgr := settings.NewSettingsManager(context.Background(), getKubeClientWithConfig(config, secretConfig), "argocd")
mgr := NewSessionManager(settingsMgr, getProjLister(), "", NewUserStateStorage(nil))
mgr.verificationDelayNoiseEnabled = false
claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"something"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}
claims.Issuer = oidcTestServer.URL
token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims)
key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey)
require.NoError(t, err)
tokenString, err := token.SignedString(key)
require.NoError(t, err)
_, _, err = mgr.VerifyToken(tokenString)
assert.NoError(t, err)
})
t.Run("OIDC provider is external, audience is not in allowed list", func(t *testing.T) {
config := map[string]string{
"url": "",
"oidc.config": fmt.Sprintf(`
name: Test
issuer: %s
clientID: xxx
clientSecret: yyy
requestedScopes: ["oidc"]
allowedAudiences:
- something-else`, oidcTestServer.URL),
"oidc.tls.insecure.skip.verify": "true", // This isn't what we're testing.
}
// This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair
// must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled.
secretConfig := map[string][]byte{
"tls.crt": utiltest.Cert,
"tls.key": utiltest.PrivateKey,
}
settingsMgr := settings.NewSettingsManager(context.Background(), getKubeClientWithConfig(config, secretConfig), "argocd")
mgr := NewSessionManager(settingsMgr, getProjLister(), "", NewUserStateStorage(nil))
mgr.verificationDelayNoiseEnabled = false
claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"something"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}
claims.Issuer = oidcTestServer.URL
token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims)
key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey)
require.NoError(t, err)
tokenString, err := token.SignedString(key)
require.NoError(t, err)
_, _, err = mgr.VerifyToken(tokenString)
require.Error(t, err)
assert.ErrorIs(t, err, common.TokenVerificationErr)
})
t.Run("OIDC provider is external, audience is not client ID, and there is no allow list", func(t *testing.T) {
config := map[string]string{
"url": "",
"oidc.config": fmt.Sprintf(`
name: Test
issuer: %s
clientID: xxx
clientSecret: yyy
requestedScopes: ["oidc"]`, oidcTestServer.URL),
"oidc.tls.insecure.skip.verify": "true", // This isn't what we're testing.
}
// This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair
// must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled.
secretConfig := map[string][]byte{
"tls.crt": utiltest.Cert,
"tls.key": utiltest.PrivateKey,
}
settingsMgr := settings.NewSettingsManager(context.Background(), getKubeClientWithConfig(config, secretConfig), "argocd")
mgr := NewSessionManager(settingsMgr, getProjLister(), "", NewUserStateStorage(nil))
mgr.verificationDelayNoiseEnabled = false
claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"something"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}
claims.Issuer = oidcTestServer.URL
token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims)
key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey)
require.NoError(t, err)
tokenString, err := token.SignedString(key)
require.NoError(t, err)
_, _, err = mgr.VerifyToken(tokenString)
require.Error(t, err)
assert.ErrorIs(t, err, common.TokenVerificationErr)
})
t.Run("OIDC provider is external, audience is specified, but allow list is empty", func(t *testing.T) {
config := map[string]string{
"url": "",
"oidc.config": fmt.Sprintf(`
name: Test
issuer: %s
clientID: xxx
clientSecret: yyy
requestedScopes: ["oidc"]
allowedAudiences: []`, oidcTestServer.URL),
"oidc.tls.insecure.skip.verify": "true", // This isn't what we're testing.
}
// This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair
// must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled.
secretConfig := map[string][]byte{
"tls.crt": utiltest.Cert,
"tls.key": utiltest.PrivateKey,
}
settingsMgr := settings.NewSettingsManager(context.Background(), getKubeClientWithConfig(config, secretConfig), "argocd")
mgr := NewSessionManager(settingsMgr, getProjLister(), "", NewUserStateStorage(nil))
mgr.verificationDelayNoiseEnabled = false
claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"something"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}
claims.Issuer = oidcTestServer.URL
token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims)
key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey)
require.NoError(t, err)
tokenString, err := token.SignedString(key)
require.NoError(t, err)
_, _, err = mgr.VerifyToken(tokenString)
require.Error(t, err)
assert.ErrorIs(t, err, common.TokenVerificationErr)
})
t.Run("OIDC provider is external, audience is not specified, token is signed with the wrong key", func(t *testing.T) {
config := map[string]string{
"url": "",
"oidc.config": fmt.Sprintf(`
name: Test
issuer: %s
clientID: xxx
clientSecret: yyy
requestedScopes: ["oidc"]`, oidcTestServer.URL),
"oidc.tls.insecure.skip.verify": "true", // This isn't what we're testing.
}
// This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair
// must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled.
secretConfig := map[string][]byte{
"tls.crt": utiltest.Cert,
"tls.key": utiltest.PrivateKey,
}
settingsMgr := settings.NewSettingsManager(context.Background(), getKubeClientWithConfig(config, secretConfig), "argocd")
mgr := NewSessionManager(settingsMgr, getProjLister(), "", NewUserStateStorage(nil))
mgr.verificationDelayNoiseEnabled = false
claims := jwt.RegisteredClaims{Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}
claims.Issuer = oidcTestServer.URL
token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims)
key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey2)
require.NoError(t, err)
tokenString, err := token.SignedString(key)
require.NoError(t, err)
_, _, err = mgr.VerifyToken(tokenString)
require.Error(t, err)
assert.ErrorIs(t, err, common.TokenVerificationErr)
})
}

View File

@@ -130,6 +130,33 @@ type Help struct {
BinaryURLs map[string]string `json:"binaryUrl,omitempty"`
}
// oidcConfig is the same as the public OIDCConfig, except the public one excludes the AllowedAudiences and the
// SkipAudienceCheckWhenTokenHasNoAudience fields.
// AllowedAudiences should be accessed via ArgoCDSettings.OAuth2AllowedAudiences.
// SkipAudienceCheckWhenTokenHasNoAudience should be accessed via ArgoCDSettings.SkipAudienceCheckWhenTokenHasNoAudience.
type oidcConfig struct {
OIDCConfig
AllowedAudiences []string `json:"allowedAudiences,omitempty"`
SkipAudienceCheckWhenTokenHasNoAudience *bool `json:"skipAudienceCheckWhenTokenHasNoAudience,omitempty"`
}
func (o *oidcConfig) toExported() *OIDCConfig {
if o == nil {
return nil
}
return &OIDCConfig{
Name: o.Name,
Issuer: o.Issuer,
ClientID: o.ClientID,
ClientSecret: o.ClientSecret,
CLIClientID: o.CLIClientID,
RequestedScopes: o.RequestedScopes,
RequestedIDTokenClaims: o.RequestedIDTokenClaims,
LogoutURL: o.LogoutURL,
RootCA: o.RootCA,
}
}
type OIDCConfig struct {
Name string `json:"name,omitempty"`
Issuer string `json:"issuer,omitempty"`
@@ -1596,24 +1623,37 @@ func UnmarshalDexConfig(config string) (map[string]interface{}, error) {
return dexCfg, err
}
func (a *ArgoCDSettings) OIDCConfig() *OIDCConfig {
func (a *ArgoCDSettings) oidcConfig() *oidcConfig {
if a.OIDCConfigRAW == "" {
return nil
}
oidcConfig, err := UnmarshalOIDCConfig(a.OIDCConfigRAW)
config, err := unmarshalOIDCConfig(a.OIDCConfigRAW)
if err != nil {
log.Warnf("invalid oidc config: %v", err)
return nil
}
oidcConfig.ClientSecret = ReplaceStringSecret(oidcConfig.ClientSecret, a.Secrets)
oidcConfig.ClientID = ReplaceStringSecret(oidcConfig.ClientID, a.Secrets)
return &oidcConfig
config.ClientSecret = ReplaceStringSecret(config.ClientSecret, a.Secrets)
config.ClientID = ReplaceStringSecret(config.ClientID, a.Secrets)
return &config
}
func UnmarshalOIDCConfig(config string) (OIDCConfig, error) {
var oidcConfig OIDCConfig
err := yaml.Unmarshal([]byte(config), &oidcConfig)
return oidcConfig, err
func (a *ArgoCDSettings) OIDCConfig() *OIDCConfig {
config := a.oidcConfig()
if config == nil {
return nil
}
return config.toExported()
}
func unmarshalOIDCConfig(configStr string) (oidcConfig, error) {
var config oidcConfig
err := yaml.Unmarshal([]byte(configStr), &config)
return config, err
}
func ValidateOIDCConfig(configStr string) error {
_, err := unmarshalOIDCConfig(configStr)
return err
}
// TLSConfig returns a tls.Config with the configured certificates
@@ -1652,6 +1692,37 @@ func (a *ArgoCDSettings) OAuth2ClientID() string {
return ""
}
// OAuth2AllowedAudiences returns a list of audiences that are allowed for the OAuth2 client. If the user has not
// explicitly configured the list of audiences (or has configured an empty list), then the OAuth2 client ID is returned
// as the only allowed audience. When using the bundled Dex, that client ID is always "argo-cd".
func (a *ArgoCDSettings) OAuth2AllowedAudiences() []string {
if config := a.oidcConfig(); config != nil {
if len(config.AllowedAudiences) == 0 {
allowedAudiences := []string{config.ClientID}
if config.CLIClientID != "" {
allowedAudiences = append(allowedAudiences, config.CLIClientID)
}
return allowedAudiences
}
return config.AllowedAudiences
}
if a.DexConfig != "" {
return []string{common.ArgoCDClientAppID, common.ArgoCDCLIClientAppID}
}
return nil
}
func (a *ArgoCDSettings) SkipAudienceCheckWhenTokenHasNoAudience() bool {
if config := a.oidcConfig(); config != nil {
if config.SkipAudienceCheckWhenTokenHasNoAudience != nil {
return *config.SkipAudienceCheckWhenTokenHasNoAudience
}
return true
}
// When using the bundled Dex, the audience check is required. Dex will always send JWTs with an audience.
return false
}
func (a *ArgoCDSettings) OAuth2ClientSecret() string {
if oidcConfig := a.OIDCConfig(); oidcConfig != nil {
return oidcConfig.ClientSecret

View File

@@ -1245,3 +1245,68 @@ rootCA: "invalid"`},
})
}
}
func Test_OAuth2AllowedAudiences(t *testing.T) {
testCases := []struct {
name string
settings *ArgoCDSettings
expected []string
}{
{
name: "Empty",
settings: &ArgoCDSettings{},
expected: []string{},
},
{
name: "OIDC configured, no audiences specified, clientID used",
settings: &ArgoCDSettings{OIDCConfigRAW: `name: Test
issuer: aaa
clientID: xxx
clientSecret: yyy
requestedScopes: ["oidc"]`},
expected: []string{"xxx"},
},
{
name: "OIDC configured, no audiences specified, clientID and cliClientID used",
settings: &ArgoCDSettings{OIDCConfigRAW: `name: Test
issuer: aaa
clientID: xxx
cliClientID: cli-xxx
clientSecret: yyy
requestedScopes: ["oidc"]`},
expected: []string{"xxx", "cli-xxx"},
},
{
name: "OIDC configured, audiences specified",
settings: &ArgoCDSettings{OIDCConfigRAW: `name: Test
issuer: aaa
clientID: xxx
clientSecret: yyy
requestedScopes: ["oidc"]
allowedAudiences: ["aud1", "aud2"]`},
expected: []string{"aud1", "aud2"},
},
{
name: "Dex configured",
settings: &ArgoCDSettings{DexConfig: `connectors:
- type: github
id: github
name: GitHub
config:
clientID: aabbccddeeff00112233
clientSecret: $dex.github.clientSecret
orgs:
- name: your-github-org
`},
expected: []string{common.ArgoCDClientAppID, common.ArgoCDCLIClientAppID},
},
}
for _, tc := range testCases {
tcc := tc
t.Run(tcc.name, func(t *testing.T) {
t.Parallel()
assert.ElementsMatch(t, tcc.expected, tcc.settings.OAuth2AllowedAudiences())
})
}
}

View File

@@ -1,18 +1,22 @@
package test
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"testing"
"github.com/golang-jwt/jwt/v4"
"github.com/stretchr/testify/require"
"gopkg.in/square/go-jose.v2"
)
// Cert is a certificate for tests. It was generated like this:
// opts := tls.CertOptions{Hosts: []string{"localhost"}, Organization: "Acme"}
// certBytes, privKey, err := tls.generatePEM(opts)
//
// opts := tls.CertOptions{Hosts: []string{"localhost"}, Organization: "Acme"}
// certBytes, privKey, err := tls.generatePEM(opts)
var Cert = []byte(`-----BEGIN CERTIFICATE-----
MIIC8zCCAdugAwIBAgIQCSoocl6e/FR4mQy1wX6NbjANBgkqhkiG9w0BAQsFADAP
MQ0wCwYDVQQKEwRBY21lMB4XDTIyMDYyMjE3Mjk1MloXDTIzMDYyMjE3Mjk1Mlow
@@ -61,6 +65,48 @@ SDpz4h+Bov5qTKkzcxuu1QWtA4M0K8Iy6IYLwb83DZEm1OsAf4i0pODz21PY/I+O
VHzjB10oYgaInHZgMUdyb6F571UdiYSB6a/IlZ3ngj5touy3VIM=
-----END RSA PRIVATE KEY-----`)
// PrivateKey2 is a second RSA key used only for tests. You can use it to see if signing a JWT with a different key
// fails validation (as it should).
var PrivateKey2 = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIG4gIBAAKCAYEAqGvlMTqPxJ844hNAneTzh9lPlYx0swai2RONOGLF0/0I9Ej5
TIgVvGykcoH3e39VGAUFd8qbLKneX3nPMjhe1+0dmLPhEGffO2ZEkhMBM0x6bhYX
XIsCXly5unN/Boosibvsd9isItsnC+4m3ELyREj1gTsCqIoZxFEq2iCPhfS7uPlQ
z8G0q0FJohNOJEXYzH96Z4xuI3zudux5PPiHNsCzoUs/X0ogda14zaolvvZPYaqg
g5zmZz6dHWnnKogsp0+Q1V3Nz1/GTCs6IDURSX+EPxst5qcin92Ft6TLOb0pu/dQ
BW90AGspoelB54iElwbmib58KBzLC8U0FZIfcuN/vOfEnv7ON4RAS/R6wKRMdPEy
Fm+Lr65QntaW2AVdxFM7EZfWLFOv741fMT3a1/l3Wou+nalxe7M+epFcn67XrkIi
fLnvg/rOUESNHmfuFIa9CAJdekM1WxCFBq6/rAxmHdnbEX3SCl0h1SrzaF336JQc
PMSNGiNjra5xO8CxAgMBAAECggGAfvBLXy5HO6fSJLrkAd2VG3fTfuDM+D3xMXGG
B9CSUDOvswbpNyB+WXT9AP0p/V+8UA1A0MfY6vHhE87oNm68NTyXCQfSgx3253su
BXbjebmTsTNfSjXPhDWZGomAXPp5lRoZoT6ihubsaBaIHY0rsgHXYB6M42CrCQcw
KBVQd2M8ta7blKrntAfSKqEoTTiDraYLKM50GLVJukKDIkwjBUZ6XQAs9HIXQvqL
SV+LcYGN1QvYTTpNgdV0b73pKGpXG8AvuwXrYFKTZeNMxPnbXd5NHLE6efuOHfeb
gYoDFy7NLSJa7DdpJIYMf0yMZQVOwdcKXiK2st+e0mUS0WHNhGKQAVc3wd+gzgtS
+s/hJk/ya/4CJwXahtbn5zhNDdbgMSt+m2LVRCIGd+JL14cd1bPySD9QL3EU7+9P
nt4S9wvu2lqa6VSK2I9tsjIgm7I7T5SUI3m+DnrpTzlpDCOqFccsSIlY5I+BD9ES
7bT57cRkyeWh5w43UQeSFhul5T0tAoHBAN7BjlT22hynPNPshNtJIj+YbAX9+MV9
FIjyPa1Say/PSXf9SvRWaTDuRWnFy4B9c12p6zwtbFjewn6OBCope26mmjVtii6t
4ABhA/v17nPUjLMQQZGIE0pHGKMpspmd3hqZcNomTtdTNy9X7NBCigJNeZR17TFm
3F2qh9oNJVbAgO54PbmFiWk0vMr0x6PWA0p3Ns/qPdu7s7EFonyOHs7f3E9MCYEd
3rp5IOJ5rzFR0acYbYhsOX4zRgMRrMYb5wKBwQDBjnlFzZVF56eK5iseEZrnay5p
CsLqxDGKr8wHFHQ8G9hGLTGOsaPd3RvAD2A7rQSNNHj2S2gv8I8DBOXzFhifE31q
Cy7Zh0HjAt6Tx5yL/lKAPMbDC3trUdITJugepR72t27UmLY0ZAX0SS8ZCRg+3dAS
Vdp3zkfOhlg3w92eQSdnU+hmr44AJL1cU+CLN7pCZgkaXzuULfs/+tPVVyeOHZX7
iA2fJ2ITRzO9XjclQ49itRJWqWcq22JqsgQ6a6cCgcBn9blxmcttd/eBiG7w0I71
UzOHEGKb+KYuy69RRpfTtlA5ebMTmYh6V5l5peA11VaULgslCKX6S+xFmA4Fh1qd
548sxDSrWGakhqKPYtWopVgM8ddIDlPCZK/w5jL+UpknnNj4VsyQ3btxkv1orMUw
EexeBzNtzO2noUDJ2TzF4g3KPb/A57ubqAs8RUUvB2B9zml8W3wHIvDX+yM8Mi/a
qMtvDrOY2NHsAUABsny67c6Ex3fHJYsnhNJ1+DfENZ0CgcBuewR983rhC/l2Lyst
Xp8suOEk1B+uIY6luvKal/JA3SP16pX+/Sar3SmZ1yz24ytV7j2dWC2AL69x6bnX
pyUmp9lOTlPPloTlLx4c/DM/NUuiJw7NBiDMgUeH5w1XcKjb6pg4gXJ/NRiw95UK
lUZhm/rIfHjXKceS+twf+IznaAk10Y82Db7gFhiAOuBQlt6aR+OqSfGYAycGvgVs
IPNTC1Aw4tfjoHc6ycmerciMXKPbk7+D9+4LaG4kuLfxIMECgcANm3mBWWJCFH3h
s2PXArzk1G9RKEmfUpfhVkeMhtD2/TMG3NPvrGpmjmPx5rf1DUxOUMJyu+B1VdZg
u0GOSkEiOfI3DxNs0GwzsL9/EYoelgGj7uc6IV9awhbzRPwro5nceGJspnWqXIVp
rawN1NFkKr5MCxl5Q4veocU94ThOlFdYgreyVX6s40ZL1eF0RvAQ+e0oFT7SfCHu
B3XwyYtAFsaO5r7oEc1Bv6oNSbE+FNJzRdjkWEIhdLVKlepil/w=
-----END RSA PRIVATE KEY-----`)
func dexMockHandler(t *testing.T, url string) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
@@ -123,6 +169,20 @@ func oidcMockHandler(t *testing.T, url string) func(http.ResponseWriter, *http.R
"claims_supported": ["sub", "aud", "exp"]
}`, url))
require.NoError(t, err)
case "/keys":
pubKey, err := jwt.ParseRSAPublicKeyFromPEM(Cert)
require.NoError(t, err)
jwks := jose.JSONWebKeySet{
Keys: []jose.JSONWebKey{
{
Key: pubKey,
},
},
}
out, err := json.Marshal(jwks)
require.NoError(t, err)
_, err = io.WriteString(w, string(out))
require.NoError(t, err)
default:
w.WriteHeader(404)
}
@@ -137,4 +197,4 @@ func GetOIDCTestServer(t *testing.T) *httptest.Server {
oidcMockHandler(t, ts.URL)(w, r)
})
return ts
}
}

View File

@@ -34,6 +34,10 @@ type settingsSource interface {
GetTrackingMethod() (string, error)
}
// https://www.rfc-editor.org/rfc/rfc3986#section-3.2.1
// https://github.com/shadow-maint/shadow/blob/master/libmisc/chkname.c#L36
const usernameRegex = `[a-zA-Z0-9_\.][a-zA-Z0-9_\.-]{0,30}[a-zA-Z0-9_\.\$-]?`
var _ settingsSource = &settings.SettingsManager{}
type ArgoCDWebhookHandler struct {
@@ -262,7 +266,8 @@ func getWebUrlRegex(webURL string) (*regexp.Regexp, error) {
regexEscapedHostname := regexp.QuoteMeta(urlObj.Hostname())
regexEscapedPath := regexp.QuoteMeta(urlObj.Path[1:])
regexpStr := fmt.Sprintf(`(?i)^(http://|https://|\w+@|ssh://(\w+@)?)%s(:[0-9]+|)[:/]%s(\.git)?$`, regexEscapedHostname, regexEscapedPath)
regexpStr := fmt.Sprintf(`(?i)^(http://|https://|%s@|ssh://(%s@)?)%s(:[0-9]+|)[:/]%s(\.git)?$`,
usernameRegex, usernameRegex, regexEscapedHostname, regexEscapedPath)
repoRegexp, err := regexp.Compile(regexpStr)
if err != nil {
return nil, fmt.Errorf("failed to compile regexp for repoURL '%s'", webURL)

View File

@@ -427,6 +427,8 @@ func Test_getWebUrlRegex(t *testing.T) {
{true, "https://example.com/org/repo", "ssh://git@example.com/org/repo", "git with protocol should match"},
{true, "https://example.com/org/repo", "ssh://git@example.com:22/org/repo", "git with port number should should match"},
{true, "https://example.com:443/org/repo", "ssh://git@example.com:22/org/repo", "https and ssh w/ different port numbers should match"},
{true, "https://example.com/org/repo", "ssh://user-name@example.com/org/repo", "valid usernames with hyphens in repo should match"},
{false, "https://example.com/org/repo", "ssh://-user-name@example.com/org/repo", "invalid usernames with hyphens in repo should not match"},
{true, "https://example.com:443/org/repo", "GIT@EXAMPLE.COM:22:ORG/REPO", "matches aren't case-sensitive"},
}
for _, testCase := range tests {