Compare commits

...

71 Commits

Author SHA1 Message Date
argo-bot
674cf8d6e2 Bump version to 2.6.8 2023-05-25 15:43:11 +00:00
argo-bot
c1dba1c764 Bump version to 2.6.8 2023-05-25 15:43:05 +00:00
Michael Crenshaw
d383379b0c Revert "Bump version to 2.6.8 (#13725)" (#13728)
This reverts commit adbb1f50c8.

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-05-24 11:18:21 -04:00
github-actions[bot]
adbb1f50c8 Bump version to 2.6.8 (#13725)
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: crenshaw-dev <crenshaw-dev@users.noreply.github.com>
2023-05-24 10:39:08 -04:00
Alexander Matyushentsev
419165a296 fix: CMPv2 does not allow symlinks to adjacent files in same git repo. Fixes #13342 (#13360) (#13724)
fix: CMPv2 does not allow symlinks to adjacent files in same git repo. Fixes #13342 (#13360)

Signed-off-by: Jiacheng Xu <xjcmaxwellcjx@gmail.com>
Co-authored-by: Jiacheng Xu <xjcmaxwellcjx@gmail.com>
2023-05-24 10:23:38 -04:00
Michael Crenshaw
19f5d43235 Revert "Bump version to 2.6.8 (#13718)" (#13721)
This reverts commit 2a433f168a.

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-05-24 10:23:28 -04:00
github-actions[bot]
2a433f168a Bump version to 2.6.8 (#13718)
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: crenshaw-dev <crenshaw-dev@users.noreply.github.com>
2023-05-24 09:36:12 -04:00
gcp-cherry-pick-bot[bot]
494e9eeb51 fix(appset): handle templating of raw JSON fields (#12947) (#12949) (#13653)
* fix(appset): handle templating of raw JSON fields (#12947)



* revert unnecessary changes



---------

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-05-18 19:57:58 -04:00
Justin Marquis
cc75f42a10 chore: upgrade redis to 7.0.11 to avoid CVE-2023-0464(release-2.6) (#13559)
Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>
2023-05-18 18:22:36 -04:00
gcp-cherry-pick-bot[bot]
876ff3035e fix: avoid acquiring lock on two mutexes at the same time to prevent deadlock (#13636) (#13649)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
Co-authored-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2023-05-18 13:05:54 -07:00
gcp-cherry-pick-bot[bot]
4d63d279d6 docs: Update kustomize resource to correct path (#13196) (#13631)
This commit updates the kustomize section to include the correct path.  Without the `\base` at the end of the path kustomize errors out trying to find a `kustomization.yaml` since there isn't one in the `ha` directory.

Signed-off-by: Chris Wiggins <5607419+cwiggs@users.noreply.github.com>
Co-authored-by: Chris Wiggins <5607419+cwiggs@users.noreply.github.com>
2023-05-18 09:12:55 -04:00
Jaideep Rao
62ae79ab5e fix: consume cluster cache deadlock fix from gitops-engine (#13613)
Signed-off-by: Jaideep Rao <jaideep.r97@gmail.com>
2023-05-17 13:39:55 -04:00
gcp-cherry-pick-bot[bot]
6f5eaff91f fix: surface errors when compressing files (#13491) (#13493)
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-05-09 12:37:02 -04:00
gcp-cherry-pick-bot[bot]
c8b4707c55 docs: fix typo (#12960) (#13437)
Signed-off-by: mikutas <23391543+mikutas@users.noreply.github.com>
Co-authored-by: Takumi Sue <23391543+mikutas@users.noreply.github.com>
2023-05-04 18:21:17 -04:00
gcp-cherry-pick-bot[bot]
09143f26a4 chore: upgrade haproxy to 2.6.12 to avoid CVE-2023-0464 (#13388) (#13400)
Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>
Co-authored-by: Justin Marquis <34fathombelow@protonmail.com>
2023-05-01 16:43:28 -04:00
gcp-cherry-pick-bot[bot]
f08375665b docs: Application Info field documentation (#10814) (#13351) (#13376)
* add Application info field documentation



* Extra Application info docs



* Added info field documentation



* Add space to comment




* docs: Add extra_info.md to table of contents



---------

Signed-off-by: Hapshanko <112761282+Hapshanko@users.noreply.github.com>
Co-authored-by: Hapshanko <112761282+Hapshanko@users.noreply.github.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-05-01 15:42:41 -04:00
gcp-cherry-pick-bot[bot]
0901195cd9 docs: s/No supported/Not supported (#13189) (#13254)
Signed-off-by: Vincent Verleye <124772102+smals-vinve@users.noreply.github.com>
Co-authored-by: Vincent Verleye <124772102+smals-vinve@users.noreply.github.com>
2023-04-16 01:34:23 -04:00
gcp-cherry-pick-bot[bot]
463e62ff80 docs: Fix wrong link to non existing page for applicationset reference (#13207) (#13248)
Signed-off-by: TheDatabaseMe <philip.haberkern@googlemail.com>
Co-authored-by: Philip Haberkern <59010269+thedatabaseme@users.noreply.github.com>
2023-04-15 14:33:43 -04:00
Alexander Matyushentsev
71e523e776 fix: --file usage is broken for 'argocd proj create' command (#13130)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2023-04-07 09:52:13 -07:00
gcp-cherry-pick-bot[bot]
61d0ef7614 fix(cli): add redis-compress flag to argocd admin dashboard command (#13055) (#13056) (#13115)
* add `redis-compress` flag to `argocd admin dashboard` command

Previously, gzip compression was disabled and not configurable,
which made it impossible to work with gzipped Redis cache.
This commit adds support for gzip compression to the ArgoCD admin dashboard.



* update dashboard docs for --redis-compress flag



* add support for REDIS_COMRESSION env in cli admin dashboard



* update flag description




* update dashboard docs



---------

Signed-off-by: Pavel Aborilov <aborilov@gmail.com>
Signed-off-by: Pavel <aborilov@gmail.com>
Co-authored-by: Pavel <aborilov@gmail.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-04-06 16:08:37 -07:00
Michael Crenshaw
d54d931f3e fix(security): upgrade go to 1.19 (#13104)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-04-04 16:50:06 -04:00
gcp-cherry-pick-bot[bot]
0c31efd158 chore: disable codeql workflow on cherry-pick branches (#12893) (#12900)
Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>
Co-authored-by: Justin Marquis <34fathombelow@protonmail.com>
2023-04-04 16:45:09 -04:00
gcp-cherry-pick-bot[bot]
a325c35320 docs: fix broken version selector (#13102) (#13106)
Signed-off-by: Harold Cheng <niuchangcun@gmail.com>
Co-authored-by: cjc7373 <niuchangcun@gmail.com>
2023-04-04 16:23:47 -04:00
gcp-cherry-pick-bot[bot]
9d56d4fa26 fix: log error when failing to get git client (#12876) (#12997)
* fix: log error when failing to get git client



* Update reposerver/repository/repository.go




* Update reposerver/repository/repository.go




---------

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: jannfis <jann@mistrust.net>
2023-03-24 11:42:22 -04:00
Steve Ramage
707342d707 fix: make webhook handler work in all configured application namespaces (#11867) (#12386)
Signed-off-by: Steve Ramage <commits@sjrx.net>
Co-authored-by: Steve Ramage <commits@sjrx.net>
2023-03-24 11:29:35 -04:00
Nobuo Takizawa
de600c0222 chore: Bump dex from v2.35.3 to v2.36.0 (#12933)
Signed-off-by: nobuyo <longzechangsheng@gmail.com>
2023-03-24 09:54:57 -04:00
gcp-cherry-pick-bot[bot]
937e88a164 fix: trigger ApplicationSet reconciliation for clusters matching cluster generators in matrix or merge generators (#12543) (#12991)
Signed-off-by: alexandre.vilain <alexandre.vilain@corp.ovh.com>
Co-authored-by: Alexandre Vilain <alexandrevilain@users.noreply.github.com>
2023-03-24 09:51:30 -04:00
gcp-cherry-pick-bot[bot]
4f3e5080a5 fix: pass env when getting param announcement (#11812) (#11815) (#12966)
* fix: pass env when getting param announcement (#11812)



* use same method as other methods



* better tests



* make sure env var tests are meaningful



---------

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-03-23 14:03:00 -04:00
gcp-cherry-pick-bot[bot]
cf74364052 chore: fix lint (#12972) (#12977)
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-03-23 13:27:35 -04:00
argo-bot
5bcd846fa1 Bump version to 2.6.7 2023-03-23 14:41:44 +00:00
argo-bot
7af7aaa08f Bump version to 2.6.7 2023-03-23 14:41:38 +00:00
Michael Crenshaw
ccb64f1c7e Merge pull request from GHSA-2q5c-qw9c-fmvq
* fix: prevent app enumeration

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

fix tests

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

better comments

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

tests for streaming API calls

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

fix logging

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

logs

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

warn

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

fix reversed arg order

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>

---------

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-03-23 09:22:05 -04:00
argo-bot
6d4de2ec5d Bump version to 2.6.6 2023-03-16 22:12:55 +00:00
argo-bot
bc13533afa Bump version to 2.6.6 2023-03-16 22:12:45 +00:00
dependabot[bot]
3260ecc729 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:23 -04:00
Michael Crenshaw
d3f81decf3 docs: fix version numbers in upgrade notes (#12896)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-03-16 12:50:41 -04:00
dependabot[bot]
25823b88d9 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:48:55 -04:00
jannfis
56a8ce5ff2 build: Enable CI checks on PRs to release branches (#12887)
Signed-off-by: jannfis <jann@mistrust.net>
2023-03-16 09:50:27 -04:00
gcp-cherry-pick-bot[bot]
153bf967e9 test: wait longer after repo server restarted to avoid errors on s390x (#12839) (#12886)
Signed-off-by: Sam Ding <samding@ca.ibm.com>
Co-authored-by: Sam Ding <samding@ca.ibm.com>
2023-03-16 09:32:59 -04:00
gcp-cherry-pick-bot[bot]
2c10c033db fix(appset): git files generator in matrix generator produces no params (#12881) (#12882)
* fix(appset): git files generator in matrix generator produces no params



* upgrade notes



* fix lint



---------

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-03-16 09:23:55 -04:00
gcp-cherry-pick-bot[bot]
e883e7498f fix: log plugin commands in a better format (#12260) (#12875)
* fix: log plugin commands in a better format



* comments



---------

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-03-15 09:49:03 -04:00
gcp-cherry-pick-bot[bot]
0c3c7e2fa9 docs: cleanup HA operator manual (#10409) (#12867)
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:40 -04:00
gcp-cherry-pick-bot[bot]
de63eb4e52 docs: fix list formatting in keycloak.md (#11061) (#12864)
Signed-off-by: Jack Henschel <jackdev@mailbox.org>
Co-authored-by: Jack Henschel <jackdev@mailbox.org>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-03-15 09:26:54 -04:00
Michael Crenshaw
a119a5cc93 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-14 17:11:25 -04:00
gcp-cherry-pick-bot[bot]
dc744bb21d docs: Post Selector moved to Generators section (#11109) (#12858)
Co-authored-by: Guðmundur Kristinn Ögmundsson <gummikr@icelandair.is>
2023-03-14 14:41:53 -04:00
dependabot[bot]
4d5f9bdb5d 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:56:43 -04:00
argo-bot
60104aca6f Bump version to 2.6.5 2023-03-14 14:06:46 +00:00
argo-bot
3ea15f05f2 Bump version to 2.6.5 2023-03-14 14:06:37 +00:00
Michael Crenshaw
d557447214 chore: upgrade https lib to avoid CVE-2022-41723
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-03-13 09:51:12 -04:00
gcp-cherry-pick-bot[bot]
67379d881b fix: ignore gitlab projects w/o repository (#12819) (#12820) (#12827)
* fix: ignore gitlab projects w/o repository (#12819)



* chore: Add Redpill Linpro to USERS.md



---------

Signed-off-by: Pip Oomen <pepijn@redpill-linpro.com>
Co-authored-by: Pip Oomen <oomen@piprograms.com>
2023-03-11 20:02:49 -05:00
gcp-cherry-pick-bot[bot]
7d335432cd docs: Provide example RBAC for API server in apps-in-any-namespace (#12341) (#12824)
Signed-off-by: jannfis <jann@mistrust.net>
Co-authored-by: jannfis <jann@mistrust.net>
2023-03-11 11:17:43 -05:00
gcp-cherry-pick-bot[bot]
f7b6b82a04 docs: Fix Jenkins guide link in understand_the_basics.md (#12814) (#12818)
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:52 -05:00
gcp-cherry-pick-bot[bot]
e81b22bc61 fix: use field-wise templating for child matrix generators (#11661) (#12287) (#12771)
* fix: use field-wise templating for child matrix generators (#11661)



* test shouldn't use go template



* Update applicationset/utils/utils.go



---------

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-03-08 13:10:30 -05:00
gcp-cherry-pick-bot[bot]
65d43364ec fix: Fix the applicationset kind typo (#12690) (#12767)
Signed-off-by: Shaw Ho <tossmilestone@gmail.com>
Co-authored-by: Shaw Ho <tossmilestone@gmail.com>
2023-03-08 09:43:51 -05:00
argo-bot
7be094f38d Bump version to 2.6.4 2023-03-07 22:35:05 +00:00
argo-bot
db2869c866 Bump version to 2.6.4 2023-03-07 22:35:00 +00:00
gcp-cherry-pick-bot[bot]
2b6d55bfe5 fix: typo in doc link (#12744) (#12760)
Signed-off-by: Noah Krause <krausenoah@gmail.com>
Co-authored-by: Noah Krause <krausenoah@gmail.com>
2023-03-07 16:40:20 -05:00
gcp-cherry-pick-bot[bot]
e81ddb0855 fix: Validate chat button url only when chatUrl is set (#12655) (#12749) (#12757)
* 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:02 -05:00
gcp-cherry-pick-bot[bot]
8dcdbb588d fix: suppress Kubernetes API deprecation warnings from application controller (#12067) (#12742)
Completely suppress warning logs only for log levels that are less than Debug.

Signed-off-by: toVersus <toversus2357@gmail.com>
Co-authored-by: Tsubasa Nagasawa <toversus2357@gmail.com>
2023-03-06 16:50:56 -05:00
gcp-cherry-pick-bot[bot]
1e7aab19aa fix: Use CredsStore for GoogleCloudCreds (#12391) (#12741)
git-ask-pass.sh is no longer supported for credentials

Signed-off-by: David Becher <becher.david@googlemail.com>
Co-authored-by: david-becher <becher.david@googlemail.com>
2023-03-06 16:45:23 -05:00
gcp-cherry-pick-bot[bot]
ec6e05afca docs: add namespace to initial-password command (#12718) (#12737)
Added " -n argocd " to avoid unexpected error.

Signed-off-by: Devarsh <devarshshah2608@gmail.com>
Co-authored-by: Devarsh <devarshshah2608@gmail.com>
2023-03-06 16:41:09 -05:00
gcp-cherry-pick-bot[bot]
09ea76364c docs: Update kustomization example (#12555) (#12738)
...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:29 -05:00
dependabot[bot]
b795fcad3d 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:37 -05:00
dependabot[bot]
896b143866 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:33:40 -05:00
gcp-cherry-pick-bot[bot]
10051833a5 docs: unset finalizer before deleting an app non-cascadingly (#10949) (#12733)
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:04 -05:00
gcp-cherry-pick-bot[bot]
705ca3c95a docs: 2.6 addendum for plugin.name (#12701) (#12704)
* 2.6 addendum for plugin.name



* Clearer doc on plugin.name

---------

Signed-off-by: Raymond Wong <61470342+rwong2888@users.noreply.github.com>
Co-authored-by: Raymond Wong <61470342+rwong2888@users.noreply.github.com>
2023-03-06 12:39:57 -05:00
gcp-cherry-pick-bot[bot]
fca7f58a93 chore: Add unit tests for the shared health resource feature and fix docs (#12715) (#12720) (#12731)
* Dummy action in



* Dummy action in



* happy happy joy joy



* will the tests fail?



* happy happy joy joy



* lua tests with relative path



* bye bye custom action



* placatin custom actions tests



* added tests and fixed docs



* added tests and fixed docs



* Update docs/operator-manual/health.md




---------

Signed-off-by: reggie <reginakagan@gmail.com>
Signed-off-by: reggie-k <reginakagan@gmail.com>
Co-authored-by: reggie-k <reginakagan@gmail.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-03-06 12:39:19 -05:00
gcp-cherry-pick-bot[bot]
85d1c0fac7 fix: ensure certificate gets updated on reload (#12076) (#12696)
* 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:45 -05:00
gcp-cherry-pick-bot[bot]
bb7ec0ff32 docs: link directly to HA manifests (#11970) (#12682)
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:04 -05:00
Justin Marquis
57f6703d08 chore: upgrade redis to 7.0.8 to avoid several CVEs (#12627)
Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>
2023-02-28 09:26:34 -05:00
gcp-cherry-pick-bot[bot]
f016977b5d chore: upgrade haproxy to 2.6.9 to avoid multiple CVEs (#12628) (#12658)
Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>
Co-authored-by: Justin Marquis <34fathombelow@protonmail.com>
2023-02-28 09:25:29 -05:00
112 changed files with 2986 additions and 797 deletions

View File

@@ -9,10 +9,11 @@ on:
pull_request:
branches:
- 'master'
- 'release-*'
env:
# Golang version to use across CI steps
GOLANG_VERSION: '1.18'
GOLANG_VERSION: '1.19.7'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -27,9 +28,9 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
- name: Setup Golang
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.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-22.04
steps:
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
- name: Setup Golang
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Restore go build cache
uses: actions/cache@6998d139ddd3e68c71e9e398d8e40b71a2f39812 # v3.2.5
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-22.04
steps:
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
- name: Setup Golang
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.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@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.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@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Install required packages
@@ -116,7 +117,7 @@ jobs:
run: |
echo "/usr/local/bin" >> $GITHUB_PATH
- name: Restore go build cache
uses: actions/cache@6998d139ddd3e68c71e9e398d8e40b71a2f39812 # v3.2.5
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
@@ -159,11 +160,11 @@ jobs:
- name: Create checkout directory
run: mkdir -p ~/go/src/github.com/argoproj
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.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@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Install required packages
@@ -183,7 +184,7 @@ jobs:
run: |
echo "/usr/local/bin" >> $GITHUB_PATH
- name: Restore go build cache
uses: actions/cache@6998d139ddd3e68c71e9e398d8e40b71a2f39812 # v3.2.5
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
@@ -214,9 +215,9 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
- name: Setup Golang
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Create symlink in GOPATH
@@ -262,14 +263,14 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
- name: Setup NodeJS
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@6998d139ddd3e68c71e9e398d8e40b71a2f39812 # v3.2.5
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ui/node_modules
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
@@ -299,12 +300,12 @@ jobs:
sonar_secret: ${{ secrets.SONAR_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
with:
fetch-depth: 0
- name: Restore node dependency cache
id: cache-dependencies
uses: actions/cache@6998d139ddd3e68c71e9e398d8e40b71a2f39812 # v3.2.5
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ui/node_modules
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
@@ -378,9 +379,9 @@ jobs:
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
- name: Setup Golang
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: GH actions workaround - Kill XSP4 process
@@ -398,7 +399,7 @@ jobs:
sudo chown runner $HOME/.kube/config
kubectl version
- name: Restore go build cache
uses: actions/cache@6998d139ddd3e68c71e9e398d8e40b71a2f39812 # v3.2.5
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
@@ -424,9 +425,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.7-alpine
docker pull redis:7.0.11-alpine
- name: Create target directory for binaries in the build-process
run: |
mkdir -p dist

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@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL

View File

@@ -10,7 +10,7 @@ on:
types: [ labeled, unlabeled, opened, synchronize, reopened ]
env:
GOLANG_VERSION: '1.18'
GOLANG_VERSION: '1.19.7'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -29,10 +29,10 @@ jobs:
env:
GOPATH: /home/runner/work/argo-cd/argo-cd
steps:
- uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
- uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
with:
path: src/github.com/argoproj/argo-cd
@@ -69,7 +69,7 @@ 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.1'

View File

@@ -12,7 +12,7 @@ on:
- "!release-v0*"
env:
GOLANG_VERSION: '1.18'
GOLANG_VERSION: '1.19.7'
permissions:
contents: read
@@ -43,7 +43,7 @@ jobs:
GIT_EMAIL: argoproj@gmail.com
steps:
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.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@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
with:
go-version: ${{ env.GOLANG_VERSION }}
@@ -219,7 +219,7 @@ 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.1'

View File

@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Build reports

View File

@@ -4,7 +4,7 @@ ARG BASE_IMAGE=docker.io/library/ubuntu:22.04
# Initial stage which pulls prepares build dependencies and CLI tooling we need for our final image
# Also used as the image in CI jobs so needs all dependencies
####################################################################################################
FROM docker.io/library/golang:1.18 AS builder
FROM docker.io/library/golang:1.19.7 AS builder
RUN echo 'deb http://deb.debian.org/debian buster-backports main' >> /etc/apt/sources.list
@@ -99,7 +99,7 @@ RUN HOST_ARCH=$TARGETARCH NODE_ENV='production' NODE_ONLINE_ENV='online' NODE_OP
####################################################################################################
# Argo CD Build stage which performs the actual build of Argo CD binaries
####################################################################################################
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.18 AS argocd-build
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.19.7 AS argocd-build
WORKDIR /go/src/github.com/argoproj/argo-cd

View File

@@ -107,6 +107,7 @@ Currently, the following organizations are **officially** using Argo CD:
1. [imaware](https://imaware.health)
1. [Indeed](https://indeed.com)
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/)
@@ -187,6 +188,7 @@ Currently, the following organizations are **officially** using Argo CD:
1. [RapidAPI](https://www.rapidapi.com/)
1. [Recreation.gov](https://www.recreation.gov/)
1. [Red Hat](https://www.redhat.com/)
1. [Redpill Linpro](https://www.redpill-linpro.com/)
1. [reev.com](https://www.reev.com/)
1. [RightRev](https://rightrev.com/)
1. [Rise](https://www.risecard.eu/)

View File

@@ -1 +1 @@
2.6.3
2.6.8

View File

@@ -2,6 +2,7 @@ package controllers
import (
"context"
"fmt"
log "github.com/sirupsen/logrus"
@@ -46,7 +47,6 @@ type addRateLimitingInterface interface {
}
func (h *clusterSecretEventHandler) queueRelatedAppGenerators(q addRateLimitingInterface, object client.Object) {
// Check for label, lookup all ApplicationSets that might match the cluster, queue them all
if object.GetLabels()[generators.ArgoCDSecretTypeLabel] != generators.ArgoCDSecretTypeCluster {
return
@@ -73,6 +73,40 @@ func (h *clusterSecretEventHandler) queueRelatedAppGenerators(q addRateLimitingI
foundClusterGenerator = true
break
}
if generator.Matrix != nil {
ok, err := nestedGeneratorsHaveClusterGenerator(generator.Matrix.Generators)
if err != nil {
h.Log.
WithFields(log.Fields{
"namespace": appSet.GetNamespace(),
"name": appSet.GetName(),
}).
WithError(err).
Error("Unable to check if ApplicationSet matrix generators have cluster generator")
}
if ok {
foundClusterGenerator = true
break
}
}
if generator.Merge != nil {
ok, err := nestedGeneratorsHaveClusterGenerator(generator.Merge.Generators)
if err != nil {
h.Log.
WithFields(log.Fields{
"namespace": appSet.GetNamespace(),
"name": appSet.GetName(),
}).
WithError(err).
Error("Unable to check if ApplicationSet merge generators have cluster generator")
}
if ok {
foundClusterGenerator = true
break
}
}
}
if foundClusterGenerator {
@@ -82,3 +116,42 @@ func (h *clusterSecretEventHandler) queueRelatedAppGenerators(q addRateLimitingI
}
}
}
// nestedGeneratorsHaveClusterGenerator iterate over provided nested generators to check if they have a cluster generator.
func nestedGeneratorsHaveClusterGenerator(generators []argoprojiov1alpha1.ApplicationSetNestedGenerator) (bool, error) {
for _, generator := range generators {
if ok, err := nestedGeneratorHasClusterGenerator(generator); ok || err != nil {
return ok, err
}
}
return false, nil
}
// nestedGeneratorHasClusterGenerator checks if the provided generator has a cluster generator.
func nestedGeneratorHasClusterGenerator(nested argoprojiov1alpha1.ApplicationSetNestedGenerator) (bool, error) {
if nested.Clusters != nil {
return true, nil
}
if nested.Matrix != nil {
nestedMatrix, err := argoprojiov1alpha1.ToNestedMatrixGenerator(nested.Matrix)
if err != nil {
return false, fmt.Errorf("unable to get nested matrix generator: %w", err)
}
if nestedMatrix != nil {
return nestedGeneratorsHaveClusterGenerator(nestedMatrix.ToMatrixGenerator().Generators)
}
}
if nested.Merge != nil {
nestedMerge, err := argoprojiov1alpha1.ToNestedMergeGenerator(nested.Merge)
if err != nil {
return false, fmt.Errorf("unable to get nested merge generator: %w", err)
}
if nestedMerge != nil {
return nestedGeneratorsHaveClusterGenerator(nestedMerge.ToMergeGenerator().Generators)
}
}
return false, nil
}

View File

@@ -6,6 +6,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
@@ -163,7 +164,6 @@ func TestClusterEventHandler(t *testing.T) {
{NamespacedName: types.NamespacedName{Namespace: "another-namespace", Name: "my-app-set"}},
},
},
{
name: "non-argo cd secret should not match",
items: []argov1alpha1.ApplicationSet{
@@ -189,6 +189,348 @@ func TestClusterEventHandler(t *testing.T) {
},
expectedRequests: []reconcile.Request{},
},
{
name: "a matrix generator with a cluster generator should produce a request",
items: []argov1alpha1.ApplicationSet{
{
ObjectMeta: v1.ObjectMeta{
Name: "my-app-set",
Namespace: "argocd",
},
Spec: argov1alpha1.ApplicationSetSpec{
Generators: []argov1alpha1.ApplicationSetGenerator{
{
Matrix: &argov1alpha1.MatrixGenerator{
Generators: []argov1alpha1.ApplicationSetNestedGenerator{
{
Clusters: &argov1alpha1.ClusterGenerator{},
},
},
},
},
},
},
},
},
secret: corev1.Secret{
ObjectMeta: v1.ObjectMeta{
Namespace: "argocd",
Name: "my-secret",
Labels: map[string]string{
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
},
},
},
expectedRequests: []reconcile.Request{{
NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"},
}},
},
{
name: "a matrix generator with non cluster generator should not match",
items: []argov1alpha1.ApplicationSet{
{
ObjectMeta: v1.ObjectMeta{
Name: "my-app-set",
Namespace: "argocd",
},
Spec: argov1alpha1.ApplicationSetSpec{
Generators: []argov1alpha1.ApplicationSetGenerator{
{
Matrix: &argov1alpha1.MatrixGenerator{
Generators: []argov1alpha1.ApplicationSetNestedGenerator{
{
List: &argov1alpha1.ListGenerator{},
},
},
},
},
},
},
},
},
secret: corev1.Secret{
ObjectMeta: v1.ObjectMeta{
Namespace: "argocd",
Name: "my-secret",
Labels: map[string]string{
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
},
},
},
expectedRequests: []reconcile.Request{},
},
{
name: "a matrix generator with a nested matrix generator containing a cluster generator should produce a request",
items: []argov1alpha1.ApplicationSet{
{
ObjectMeta: v1.ObjectMeta{
Name: "my-app-set",
Namespace: "argocd",
},
Spec: argov1alpha1.ApplicationSetSpec{
Generators: []argov1alpha1.ApplicationSetGenerator{
{
Matrix: &argov1alpha1.MatrixGenerator{
Generators: []argov1alpha1.ApplicationSetNestedGenerator{
{
Matrix: &apiextensionsv1.JSON{
Raw: []byte(
`{
"generators": [
{
"clusters": {
"selector": {
"matchLabels": {
"argocd.argoproj.io/secret-type": "cluster"
}
}
}
}
]
}`,
),
},
},
},
},
},
},
},
},
},
secret: corev1.Secret{
ObjectMeta: v1.ObjectMeta{
Namespace: "argocd",
Name: "my-secret",
Labels: map[string]string{
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
},
},
},
expectedRequests: []reconcile.Request{{
NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"},
}},
},
{
name: "a matrix generator with a nested matrix generator containing non cluster generator should not match",
items: []argov1alpha1.ApplicationSet{
{
ObjectMeta: v1.ObjectMeta{
Name: "my-app-set",
Namespace: "argocd",
},
Spec: argov1alpha1.ApplicationSetSpec{
Generators: []argov1alpha1.ApplicationSetGenerator{
{
Matrix: &argov1alpha1.MatrixGenerator{
Generators: []argov1alpha1.ApplicationSetNestedGenerator{
{
Matrix: &apiextensionsv1.JSON{
Raw: []byte(
`{
"generators": [
{
"list": {
"elements": [
"a",
"b"
]
}
}
]
}`,
),
},
},
},
},
},
},
},
},
},
secret: corev1.Secret{
ObjectMeta: v1.ObjectMeta{
Namespace: "argocd",
Name: "my-secret",
Labels: map[string]string{
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
},
},
},
expectedRequests: []reconcile.Request{},
},
{
name: "a merge generator with a cluster generator should produce a request",
items: []argov1alpha1.ApplicationSet{
{
ObjectMeta: v1.ObjectMeta{
Name: "my-app-set",
Namespace: "argocd",
},
Spec: argov1alpha1.ApplicationSetSpec{
Generators: []argov1alpha1.ApplicationSetGenerator{
{
Merge: &argov1alpha1.MergeGenerator{
Generators: []argov1alpha1.ApplicationSetNestedGenerator{
{
Clusters: &argov1alpha1.ClusterGenerator{},
},
},
},
},
},
},
},
},
secret: corev1.Secret{
ObjectMeta: v1.ObjectMeta{
Namespace: "argocd",
Name: "my-secret",
Labels: map[string]string{
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
},
},
},
expectedRequests: []reconcile.Request{{
NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"},
}},
},
{
name: "a matrix generator with non cluster generator should not match",
items: []argov1alpha1.ApplicationSet{
{
ObjectMeta: v1.ObjectMeta{
Name: "my-app-set",
Namespace: "argocd",
},
Spec: argov1alpha1.ApplicationSetSpec{
Generators: []argov1alpha1.ApplicationSetGenerator{
{
Merge: &argov1alpha1.MergeGenerator{
Generators: []argov1alpha1.ApplicationSetNestedGenerator{
{
List: &argov1alpha1.ListGenerator{},
},
},
},
},
},
},
},
},
secret: corev1.Secret{
ObjectMeta: v1.ObjectMeta{
Namespace: "argocd",
Name: "my-secret",
Labels: map[string]string{
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
},
},
},
expectedRequests: []reconcile.Request{},
},
{
name: "a merge generator with a nested merge generator containing a cluster generator should produce a request",
items: []argov1alpha1.ApplicationSet{
{
ObjectMeta: v1.ObjectMeta{
Name: "my-app-set",
Namespace: "argocd",
},
Spec: argov1alpha1.ApplicationSetSpec{
Generators: []argov1alpha1.ApplicationSetGenerator{
{
Merge: &argov1alpha1.MergeGenerator{
Generators: []argov1alpha1.ApplicationSetNestedGenerator{
{
Merge: &apiextensionsv1.JSON{
Raw: []byte(
`{
"generators": [
{
"clusters": {
"selector": {
"matchLabels": {
"argocd.argoproj.io/secret-type": "cluster"
}
}
}
}
]
}`,
),
},
},
},
},
},
},
},
},
},
secret: corev1.Secret{
ObjectMeta: v1.ObjectMeta{
Namespace: "argocd",
Name: "my-secret",
Labels: map[string]string{
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
},
},
},
expectedRequests: []reconcile.Request{{
NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"},
}},
},
{
name: "a merge generator with a nested merge generator containing non cluster generator should not match",
items: []argov1alpha1.ApplicationSet{
{
ObjectMeta: v1.ObjectMeta{
Name: "my-app-set",
Namespace: "argocd",
},
Spec: argov1alpha1.ApplicationSetSpec{
Generators: []argov1alpha1.ApplicationSetGenerator{
{
Merge: &argov1alpha1.MergeGenerator{
Generators: []argov1alpha1.ApplicationSetNestedGenerator{
{
Merge: &apiextensionsv1.JSON{
Raw: []byte(
`{
"generators": [
{
"list": {
"elements": [
"a",
"b"
]
}
}
]
}`,
),
},
},
},
},
},
},
},
},
},
secret: corev1.Secret{
ObjectMeta: v1.ObjectMeta{
Namespace: "argocd",
Name: "my-secret",
Labels: map[string]string{
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
},
},
},
expectedRequests: []reconcile.Request{},
},
}
for _, test := range tests {

View File

@@ -2,7 +2,6 @@ package generators
import (
"fmt"
"encoding/json"
"reflect"
"github.com/argoproj/argo-cd/v2/applicationset/utils"
@@ -25,7 +24,7 @@ type TransformResult struct {
Template argoprojiov1alpha1.ApplicationSetTemplate
}
//Transform a spec generator to list of paramSets and a template
// Transform a spec generator to list of paramSets and a template
func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, allGenerators map[string]Generator, baseTemplate argoprojiov1alpha1.ApplicationSetTemplate, appSet *argoprojiov1alpha1.ApplicationSet, genParams map[string]interface{}) ([]TransformResult, error) {
selector, err := metav1.LabelSelectorAsSelector(requestedGenerator.Selector)
if err != nil {
@@ -132,27 +131,15 @@ func mergeGeneratorTemplate(g Generator, requestedGenerator *argoprojiov1alpha1.
return *dest, err
}
// Currently for Matrix Generator. Allows interpolating the matrix's 2nd child generator with values from the 1st child generator
// InterpolateGenerator allows interpolating the matrix's 2nd child generator with values from the 1st child generator
// "params" parameter is an array, where each index corresponds to a generator. Each index contains a map w/ that generator's parameters.
func InterpolateGenerator(requestedGenerator *argoprojiov1alpha1.ApplicationSetGenerator, params map[string]interface{}, useGoTemplate bool) (argoprojiov1alpha1.ApplicationSetGenerator, error) {
interpolatedGenerator := requestedGenerator.DeepCopy()
tmplBytes, err := json.Marshal(interpolatedGenerator)
if err != nil {
log.WithError(err).WithField("requestedGenerator", interpolatedGenerator).Error("error marshalling requested generator for interpolation")
return *interpolatedGenerator, err
}
render := utils.Render{}
replacedTmplStr, err := render.Replace(string(tmplBytes), params, useGoTemplate)
interpolatedGenerator, err := render.RenderGeneratorParams(requestedGenerator, params, useGoTemplate)
if err != nil {
log.WithError(err).WithField("interpolatedGeneratorString", replacedTmplStr).Error("error interpolating generator with other generator's parameter")
log.WithError(err).WithField("interpolatedGenerator", interpolatedGenerator).Error("error interpolating generator with other generator's parameter")
return *interpolatedGenerator, err
}
err = json.Unmarshal([]byte(replacedTmplStr), interpolatedGenerator)
if err != nil {
log.WithError(err).WithField("requestedGenerator", interpolatedGenerator).Error("error unmarshalling requested generator for interpolation")
return *interpolatedGenerator, err
}
return *interpolatedGenerator, nil
}

View File

@@ -6,9 +6,11 @@ import (
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
testutils "github.com/argoproj/argo-cd/v2/applicationset/utils/test"
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/stretchr/testify/mock"
@@ -159,8 +161,8 @@ func getMockClusterGenerator() Generator {
}
func getMockGitGenerator() Generator {
argoCDServiceMock := argoCDServiceMock{mock: &mock.Mock{}}
argoCDServiceMock.mock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return([]string{"app1", "app2", "app_3", "p1/app4"}, nil)
argoCDServiceMock := testutils.ArgoCDServiceMock{Mock: &mock.Mock{}}
argoCDServiceMock.Mock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return([]string{"app1", "app2", "app_3", "p1/app4"}, nil)
var gitGenerator = NewGitGenerator(argoCDServiceMock)
return gitGenerator
}
@@ -248,6 +250,60 @@ func TestInterpolateGenerator(t *testing.T) {
Path: "{{server}}",
}
requestedGenerator = &argoprojiov1alpha1.ApplicationSetGenerator{
Git: &argoprojiov1alpha1.GitGenerator{
Files: append([]argoprojiov1alpha1.GitFileGeneratorItem{}, fileNamePath, fileServerPath),
Template: argoprojiov1alpha1.ApplicationSetTemplate{},
},
}
clusterGeneratorParams := map[string]interface{}{
"name": "production_01/west", "server": "https://production-01.example.com",
}
interpolatedGenerator, err = InterpolateGenerator(requestedGenerator, clusterGeneratorParams, false)
if err != nil {
log.WithError(err).WithField("requestedGenerator", requestedGenerator).Error("error interpolating Generator")
return
}
assert.Equal(t, "production_01/west", interpolatedGenerator.Git.Files[0].Path)
assert.Equal(t, "https://production-01.example.com", interpolatedGenerator.Git.Files[1].Path)
}
func TestInterpolateGenerator_go(t *testing.T) {
requestedGenerator := &argoprojiov1alpha1.ApplicationSetGenerator{
Clusters: &argoprojiov1alpha1.ClusterGenerator{
Selector: metav1.LabelSelector{
MatchLabels: map[string]string{
"argocd.argoproj.io/secret-type": "cluster",
"path-basename": "{{base .path.path}}",
"path-zero": "{{index .path.segments 0}}",
"path-full": "{{.path.path}}",
"kubernetes.io/environment": `{{default "foo" .my_label}}`,
}},
},
}
gitGeneratorParams := map[string]interface{}{
"path": map[string]interface{}{
"path": "p1/p2/app3",
"segments": []string{"p1", "p2", "app3"},
},
}
interpolatedGenerator, err := InterpolateGenerator(requestedGenerator, gitGeneratorParams, true)
require.NoError(t, err)
if err != nil {
log.WithError(err).WithField("requestedGenerator", requestedGenerator).Error("error interpolating Generator")
return
}
assert.Equal(t, "app3", interpolatedGenerator.Clusters.Selector.MatchLabels["path-basename"])
assert.Equal(t, "p1", interpolatedGenerator.Clusters.Selector.MatchLabels["path-zero"])
assert.Equal(t, "p1/p2/app3", interpolatedGenerator.Clusters.Selector.MatchLabels["path-full"])
fileNamePath := argoprojiov1alpha1.GitFileGeneratorItem{
Path: "{{.name}}",
}
fileServerPath := argoprojiov1alpha1.GitFileGeneratorItem{
Path: "{{.server}}",
}
requestedGenerator = &argoprojiov1alpha1.ApplicationSetGenerator{
Git: &argoprojiov1alpha1.GitGenerator{
Files: append([]argoprojiov1alpha1.GitFileGeneratorItem{}, fileNamePath, fileServerPath),

View File

@@ -58,9 +58,9 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic
var err error
var res []map[string]interface{}
if appSetGenerator.Git.Directories != nil {
if len(appSetGenerator.Git.Directories) != 0 {
res, err = g.generateParamsForGitDirectories(appSetGenerator, appSet.Spec.GoTemplate)
} else if appSetGenerator.Git.Files != nil {
} else if len(appSetGenerator.Git.Files) != 0 {
res, err = g.generateParamsForGitFiles(appSetGenerator, appSet.Spec.GoTemplate)
} else {
return nil, EmptyAppSetGeneratorError

View File

@@ -1,7 +1,6 @@
package generators
import (
"context"
"fmt"
"testing"
@@ -9,6 +8,7 @@ import (
"github.com/stretchr/testify/mock"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
testutils "github.com/argoproj/argo-cd/v2/applicationset/utils/test"
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
)
@@ -20,33 +20,6 @@ import (
// return io.NewCloser(func() error { return nil }), c.RepoServerServiceClient, nil
// }
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)
}
func Test_generateParamsFromGitFile(t *testing.T) {
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
foo:
@@ -271,9 +244,9 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
t.Run(testCaseCopy.name, func(t *testing.T) {
t.Parallel()
argoCDServiceMock := argoCDServiceMock{mock: &mock.Mock{}}
argoCDServiceMock := testutils.ArgoCDServiceMock{Mock: &mock.Mock{}}
argoCDServiceMock.mock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
argoCDServiceMock.Mock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
var gitGenerator = NewGitGenerator(argoCDServiceMock)
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
@@ -301,7 +274,7 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
assert.Equal(t, testCaseCopy.expected, got)
}
argoCDServiceMock.mock.AssertExpectations(t)
argoCDServiceMock.Mock.AssertExpectations(t)
})
}
}
@@ -566,9 +539,9 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) {
t.Run(testCaseCopy.name, func(t *testing.T) {
t.Parallel()
argoCDServiceMock := argoCDServiceMock{mock: &mock.Mock{}}
argoCDServiceMock := testutils.ArgoCDServiceMock{Mock: &mock.Mock{}}
argoCDServiceMock.mock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
argoCDServiceMock.Mock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
var gitGenerator = NewGitGenerator(argoCDServiceMock)
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
@@ -597,7 +570,7 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) {
assert.Equal(t, testCaseCopy.expected, got)
}
argoCDServiceMock.mock.AssertExpectations(t)
argoCDServiceMock.Mock.AssertExpectations(t)
})
}
@@ -857,8 +830,8 @@ cluster:
t.Run(testCaseCopy.name, func(t *testing.T) {
t.Parallel()
argoCDServiceMock := argoCDServiceMock{mock: &mock.Mock{}}
argoCDServiceMock.mock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
argoCDServiceMock := testutils.ArgoCDServiceMock{Mock: &mock.Mock{}}
argoCDServiceMock.Mock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
Return(testCaseCopy.repoFileContents, testCaseCopy.repoPathsError)
var gitGenerator = NewGitGenerator(argoCDServiceMock)
@@ -887,7 +860,7 @@ cluster:
assert.ElementsMatch(t, testCaseCopy.expected, got)
}
argoCDServiceMock.mock.AssertExpectations(t)
argoCDServiceMock.Mock.AssertExpectations(t)
})
}
}
@@ -1206,8 +1179,8 @@ cluster:
t.Run(testCaseCopy.name, func(t *testing.T) {
t.Parallel()
argoCDServiceMock := argoCDServiceMock{mock: &mock.Mock{}}
argoCDServiceMock.mock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
argoCDServiceMock := testutils.ArgoCDServiceMock{Mock: &mock.Mock{}}
argoCDServiceMock.Mock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
Return(testCaseCopy.repoFileContents, testCaseCopy.repoPathsError)
var gitGenerator = NewGitGenerator(argoCDServiceMock)
@@ -1237,7 +1210,7 @@ cluster:
assert.ElementsMatch(t, testCaseCopy.expected, got)
}
argoCDServiceMock.mock.AssertExpectations(t)
argoCDServiceMock.Mock.AssertExpectations(t)
})
}
}

View File

@@ -5,6 +5,7 @@ import (
"testing"
"time"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@@ -16,6 +17,7 @@ import (
"github.com/stretchr/testify/mock"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
testutils "github.com/argoproj/argo-cd/v2/applicationset/utils/test"
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
)
@@ -28,7 +30,7 @@ func TestMatrixGenerate(t *testing.T) {
}
listGenerator := &argoprojiov1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "Cluster","url": "Url"}`)}},
Elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "Cluster","url": "Url", "templated": "test-{{path.basenameNormalized}}"}`)}},
}
testCases := []struct {
@@ -48,8 +50,8 @@ func TestMatrixGenerate(t *testing.T) {
},
},
expected: []map[string]interface{}{
{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "cluster": "Cluster", "url": "Url"},
{"path": "app2", "path.basename": "app2", "path.basenameNormalized": "app2", "cluster": "Cluster", "url": "Url"},
{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "cluster": "Cluster", "url": "Url", "templated": "test-app1"},
{"path": "app2", "path.basename": "app2", "path.basenameNormalized": "app2", "cluster": "Cluster", "url": "Url", "templated": "test-app2"},
},
},
{
@@ -857,3 +859,72 @@ func (g *generatorMock) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.Appl
return args.Get(0).(time.Duration)
}
func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) {
// Given a matrix generator over a list generator and a git files generator, the nested git files generator should
// be treated as a files generator, and it should produce parameters.
// This tests for a specific bug where a nested git files generator was being treated as a directory generator. This
// happened because, when the matrix generator was being processed, the nested git files generator was being
// interpolated by the deeplyReplace function. That function cannot differentiate between a nil slice and an empty
// slice. So it was replacing the `Directories` field with an empty slice, which the ApplicationSet controller
// interpreted as meaning this was a directory generator, not a files generator.
// Now instead of checking for nil, we check whether the field is a non-empty slice. This test prevents a regression
// of that bug.
listGeneratorMock := &generatorMock{}
listGeneratorMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), mock.AnythingOfType("*v1alpha1.ApplicationSet")).Return([]map[string]interface{}{
{"some": "value"},
}, nil)
listGeneratorMock.On("GetTemplate", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator")).Return(&argoprojiov1alpha1.ApplicationSetTemplate{})
gitGeneratorSpec := &argoprojiov1alpha1.GitGenerator{
RepoURL: "https://git.example.com",
Files: []argoprojiov1alpha1.GitFileGeneratorItem{
{Path: "some/path.json"},
},
}
repoServiceMock := testutils.ArgoCDServiceMock{Mock: &mock.Mock{}}
repoServiceMock.Mock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(map[string][]byte{
"some/path.json": []byte("test: content"),
}, nil)
gitGenerator := NewGitGenerator(repoServiceMock)
matrixGenerator := NewMatrixGenerator(map[string]Generator{
"List": listGeneratorMock,
"Git": gitGenerator,
})
matrixGeneratorSpec := &argoprojiov1alpha1.MatrixGenerator{
Generators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
{
List: &argoprojiov1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{
{
Raw: []byte(`{"some": "value"}`),
},
},
},
},
{
Git: gitGeneratorSpec,
},
},
}
params, err := matrixGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{
Matrix: matrixGeneratorSpec,
}, &argoprojiov1alpha1.ApplicationSet{})
require.NoError(t, err)
assert.Equal(t, []map[string]interface{}{{
"path": "some",
"path.basename": "some",
"path.basenameNormalized": "some",
"path.filename": "path.json",
"path.filenameNormalized": "path.json",
"path[0]": "some",
"some": "value",
"test": "content",
}}, params)
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"os"
"net/http"
pathpkg "path"
gitlab "github.com/xanzy/go-gitlab"
@@ -144,7 +145,11 @@ func (g *GitlabProvider) listBranches(_ context.Context, repo *Repository) ([]gi
branches := []gitlab.Branch{}
// If we don't specifically want to query for all branches, just use the default branch and call it a day.
if !g.allBranches {
gitlabBranch, _, err := g.client.Branches.GetBranch(repo.RepositoryId, repo.Branch, nil)
gitlabBranch, resp, err := g.client.Branches.GetBranch(repo.RepositoryId, repo.Branch, nil)
// 404s are not an error here, just a normal false.
if resp != nil && resp.StatusCode == http.StatusNotFound {
return []gitlab.Branch{}, nil
}
if err != nil {
return nil, err
}
@@ -157,6 +162,10 @@ func (g *GitlabProvider) listBranches(_ context.Context, repo *Repository) ([]gi
}
for {
gitlabBranches, resp, err := g.client.Branches.ListBranches(repo.RepositoryId, opt)
// 404s are not an error here, just a normal false.
if resp != nil && resp.StatusCode == http.StatusNotFound {
return []gitlab.Branch{}, nil
}
if err != nil {
return nil, err
}

View File

@@ -274,6 +274,8 @@ func gitlabMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) {
if err != nil {
t.Fail()
}
case "/api/v4/projects/27084533/repository/branches/foo":
w.WriteHeader(http.StatusNotFound)
default:
_, err := io.WriteString(w, `[]`)
if err != nil {
@@ -391,3 +393,29 @@ func TestGitlabHasPath(t *testing.T) {
})
}
}
func TestGitlabGetBranches(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gitlabMockHandler(t)(w, r)
}))
host, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, false, true)
repo := &Repository{
RepositoryId: 27084533,
Branch: "master",
}
t.Run("branch exists", func(t *testing.T) {
repos, err := host.GetBranches(context.Background(), repo)
assert.Nil(t, err)
assert.Equal(t, repos[0].Branch, "master")
})
repo2 := &Repository{
RepositoryId: 27084533,
Branch: "foo",
}
t.Run("unknown branch", func(t *testing.T) {
_, err := host.GetBranches(context.Background(), repo2)
assert.NoError(t, err)
})
}

View File

@@ -0,0 +1,34 @@
package test
import (
"context"
"github.com/stretchr/testify/mock"
)
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

@@ -96,6 +96,25 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri
// specific case time
if currentType == "time.Time" {
copy.Field(i).Set(original.Field(i))
} else if currentType == "Raw.k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" {
var unmarshaled interface{}
originalBytes := original.Field(i).Bytes()
err := json.Unmarshal(originalBytes, &unmarshaled)
if err != nil {
return fmt.Errorf("failed to unmarshal JSON field: %w", err)
}
jsonOriginal := reflect.ValueOf(&unmarshaled)
jsonCopy := reflect.New(jsonOriginal.Type()).Elem()
err = r.deeplyReplace(jsonCopy, jsonOriginal, replaceMap, useGoTemplate)
if err != nil {
return fmt.Errorf("failed to deeply replace JSON field contents: %w", err)
}
jsonCopyInterface := jsonCopy.Interface().(*interface{})
data, err := json.Marshal(jsonCopyInterface)
if err != nil {
return fmt.Errorf("failed to marshal templated JSON field: %w", err)
}
copy.Field(i).Set(reflect.ValueOf(data))
} else if err := r.deeplyReplace(copy.Field(i), original.Field(i), replaceMap, useGoTemplate); err != nil {
return err
}
@@ -174,7 +193,7 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri
func (r *Render) RenderTemplateParams(tmpl *argoappsv1.Application, syncPolicy *argoappsv1.ApplicationSetSyncPolicy, params map[string]interface{}, useGoTemplate bool) (*argoappsv1.Application, error) {
if tmpl == nil {
return nil, fmt.Errorf("application template is empty ")
return nil, fmt.Errorf("application template is empty")
}
if len(params) == 0 {
@@ -204,6 +223,27 @@ func (r *Render) RenderTemplateParams(tmpl *argoappsv1.Application, syncPolicy *
return replacedTmpl, nil
}
func (r *Render) RenderGeneratorParams(gen *argoappsv1.ApplicationSetGenerator, params map[string]interface{}, useGoTemplate bool) (*argoappsv1.ApplicationSetGenerator, error) {
if gen == nil {
return nil, fmt.Errorf("generator is empty")
}
if len(params) == 0 {
return gen, nil
}
original := reflect.ValueOf(gen)
copy := reflect.New(original.Type()).Elem()
if err := r.deeplyReplace(copy, original, params, useGoTemplate); err != nil {
return nil, fmt.Errorf("failed to replace parameters in generator: %w", err)
}
replacedGen := copy.Interface().(*argoappsv1.ApplicationSetGenerator)
return replacedGen, nil
}
var isTemplatedRegex = regexp.MustCompile(".*{{.*}}.*")
// Replace executes basic string substitution of a template with replacement values.

View File

@@ -271,6 +271,16 @@
"description": "the application's namespace.",
"name": "appNamespace",
"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": {
@@ -585,6 +595,16 @@
"description": "the application's namespace.",
"name": "appNamespace",
"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": {
@@ -3670,6 +3690,16 @@
"description": "the application's namespace.",
"name": "appNamespace",
"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

@@ -9,13 +9,16 @@ import (
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/initialize"
"github.com/argoproj/argo-cd/v2/common"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
"github.com/argoproj/argo-cd/v2/util/cache"
"github.com/argoproj/argo-cd/v2/util/env"
"github.com/argoproj/argo-cd/v2/util/errors"
)
func NewDashboardCommand() *cobra.Command {
var (
port int
address string
port int
address string
compressionStr string
)
cmd := &cobra.Command{
Use: "dashboard",
@@ -23,7 +26,9 @@ func NewDashboardCommand() *cobra.Command {
Run: func(cmd *cobra.Command, args []string) {
ctx := cmd.Context()
errors.CheckError(headless.StartLocalServer(ctx, &argocdclient.ClientOptions{Core: true}, initialize.RetrieveContextIfChanged(cmd.Flag("context")), &port, &address))
compression, err := cache.CompressionTypeFromString(compressionStr)
errors.CheckError(err)
errors.CheckError(headless.StartLocalServer(ctx, &argocdclient.ClientOptions{Core: true}, initialize.RetrieveContextIfChanged(cmd.Flag("context")), &port, &address, compression))
println(fmt.Sprintf("Argo CD UI is available at http://%s:%d", address, port))
<-ctx.Done()
},
@@ -31,5 +36,6 @@ func NewDashboardCommand() *cobra.Command {
initialize.InitCommand(cmd)
cmd.Flags().IntVar(&port, "port", common.DefaultPortAPIServer, "Listen on given port")
cmd.Flags().StringVar(&address, "address", common.DefaultAddressAPIServer, "Listen on given address")
cmd.Flags().StringVar(&compressionStr, "redis-compress", env.StringFromEnv("REDIS_COMPRESSION", string(cache.RedisCompressionNone)), "Enable this if the application controller is configured with redis compression enabled. (possible values: none, gzip)")
return cmd
}

View File

@@ -165,7 +165,9 @@ func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.
// Get app before creating to see if it is being updated or no change
existing, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &app.Name})
if grpc.UnwrapGRPCStatus(err).Code() != codes.NotFound {
unwrappedError := grpc.UnwrapGRPCStatus(err).Code()
// As part of the fix for CVE-2022-41354, the API will return Permission Denied when an app does not exist.
if unwrappedError != codes.NotFound && unwrappedError != codes.PermissionDenied {
errors.CheckError(err)
}

View File

@@ -38,11 +38,12 @@ import (
)
type forwardCacheClient struct {
namespace string
context string
init sync.Once
client cache.CacheClient
err error
namespace string
context string
init sync.Once
client cache.CacheClient
compression cache.RedisCompressionType
err error
}
func (c *forwardCacheClient) doLazy(action func(client cache.CacheClient) error) error {
@@ -58,7 +59,7 @@ func (c *forwardCacheClient) doLazy(action func(client cache.CacheClient) error)
}
redisClient := redis.NewClient(&redis.Options{Addr: fmt.Sprintf("localhost:%d", redisPort)})
c.client = cache.NewRedisCache(redisClient, time.Hour, cache.RedisCompressionNone)
c.client = cache.NewRedisCache(redisClient, time.Hour, c.compression)
})
if c.err != nil {
return c.err
@@ -139,7 +140,7 @@ func testAPI(ctx context.Context, clientOpts *apiclient.ClientOptions) error {
// StartLocalServer allows executing command in a headless mode: on the fly starts Argo CD API server and
// changes provided client options to use started API server port
func StartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions, ctxStr string, port *int, address *string) error {
func StartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions, ctxStr string, port *int, address *string, compression cache.RedisCompressionType) error {
flags := pflag.NewFlagSet("tmp", pflag.ContinueOnError)
clientConfig := cli.AddKubectlFlagsToSet(flags)
startInProcessAPI := clientOpts.Core
@@ -200,7 +201,7 @@ func StartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions,
if err != nil {
return err
}
appstateCache := appstatecache.NewCache(cache.NewCache(&forwardCacheClient{namespace: namespace, context: ctxStr}), time.Hour)
appstateCache := appstatecache.NewCache(cache.NewCache(&forwardCacheClient{namespace: namespace, context: ctxStr, compression: compression}), time.Hour)
srv := server.NewServer(ctx, server.ArgoCDServerOpts{
EnableGZip: false,
Namespace: namespace,
@@ -243,7 +244,7 @@ func NewClientOrDie(opts *apiclient.ClientOptions, c *cobra.Command) apiclient.C
ctx := c.Context()
ctxStr := initialize.RetrieveContextIfChanged(c.Flag("context"))
err := StartLocalServer(ctx, opts, ctxStr, nil, nil)
err := StartLocalServer(ctx, opts, ctxStr, nil, nil, cache.RedisCompressionNone)
if err != nil {
log.Fatal(err)
}

View File

@@ -138,7 +138,10 @@ func readProjFromURI(fileURL string, proj *v1alpha1.AppProject) error {
} else {
err = config.UnmarshalRemoteFile(fileURL, &proj)
}
return fmt.Errorf("error reading proj from uri: %w", err)
if err != nil {
return fmt.Errorf("error reading proj from uri: %w", err)
}
return nil
}
func SetProjSpecOptions(flags *pflag.FlagSet, spec *v1alpha1.AppProjectSpec, projOpts *ProjectOpts) int {

View File

@@ -9,8 +9,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
@@ -355,7 +378,7 @@ func (s *Service) GetParametersAnnouncement(stream apiclient.ConfigManagementPlu
return fmt.Errorf("illegal appPath: out of workDir bound")
}
repoResponse, err := getParametersAnnouncement(bufferedCtx, appPath, s.initConstants.PluginConfig.Spec.Parameters.Static, s.initConstants.PluginConfig.Spec.Parameters.Dynamic)
repoResponse, err := getParametersAnnouncement(bufferedCtx, appPath, s.initConstants.PluginConfig.Spec.Parameters.Static, s.initConstants.PluginConfig.Spec.Parameters.Dynamic, metadata.GetEnv())
if err != nil {
return fmt.Errorf("get parameters announcement error: %w", err)
}
@@ -367,11 +390,12 @@ func (s *Service) GetParametersAnnouncement(stream apiclient.ConfigManagementPlu
return nil
}
func getParametersAnnouncement(ctx context.Context, appDir string, announcements []*repoclient.ParameterAnnouncement, command Command) (*apiclient.ParametersAnnouncementResponse, error) {
func getParametersAnnouncement(ctx context.Context, appDir string, announcements []*repoclient.ParameterAnnouncement, command Command, envEntries []*apiclient.EnvEntry) (*apiclient.ParametersAnnouncementResponse, error) {
augmentedAnnouncements := announcements
if len(command.Command) > 0 {
stdout, err := runCommand(ctx, command, appDir, os.Environ())
env := append(os.Environ(), environ(envEntries)...)
stdout, err := runCommand(ctx, command, appDir, env)
if err != nil {
return nil, fmt.Errorf("error executing dynamic parameter output command: %w", err)
}

View File

@@ -6,6 +6,7 @@ import (
"fmt"
"io"
"os"
"os/exec"
"path"
"path/filepath"
"testing"
@@ -380,7 +381,7 @@ func Test_getParametersAnnouncement_empty_command(t *testing.T) {
Command: []string{"echo"},
Args: []string{`[]`},
}
res, err := getParametersAnnouncement(context.Background(), "", *static, command)
res, err := getParametersAnnouncement(context.Background(), "", *static, command, []*apiclient.EnvEntry{})
require.NoError(t, err)
assert.Equal(t, []*repoclient.ParameterAnnouncement{{Name: "static-a"}, {Name: "static-b"}}, res.ParameterAnnouncements)
}
@@ -394,7 +395,7 @@ func Test_getParametersAnnouncement_no_command(t *testing.T) {
err := yaml.Unmarshal([]byte(staticYAML), static)
require.NoError(t, err)
command := Command{}
res, err := getParametersAnnouncement(context.Background(), "", *static, command)
res, err := getParametersAnnouncement(context.Background(), "", *static, command, []*apiclient.EnvEntry{})
require.NoError(t, err)
assert.Equal(t, []*repoclient.ParameterAnnouncement{{Name: "static-a"}, {Name: "static-b"}}, res.ParameterAnnouncements)
}
@@ -411,7 +412,7 @@ func Test_getParametersAnnouncement_static_and_dynamic(t *testing.T) {
Command: []string{"echo"},
Args: []string{`[{"name": "dynamic-a"}, {"name": "dynamic-b"}]`},
}
res, err := getParametersAnnouncement(context.Background(), "", *static, command)
res, err := getParametersAnnouncement(context.Background(), "", *static, command, []*apiclient.EnvEntry{})
require.NoError(t, err)
expected := []*repoclient.ParameterAnnouncement{
{Name: "dynamic-a"},
@@ -427,7 +428,7 @@ func Test_getParametersAnnouncement_invalid_json(t *testing.T) {
Command: []string{"echo"},
Args: []string{`[`},
}
_, err := getParametersAnnouncement(context.Background(), "", []*repoclient.ParameterAnnouncement{}, command)
_, err := getParametersAnnouncement(context.Background(), "", []*repoclient.ParameterAnnouncement{}, command, []*apiclient.EnvEntry{})
assert.Error(t, err)
assert.Contains(t, err.Error(), "unexpected end of JSON input")
}
@@ -437,7 +438,7 @@ func Test_getParametersAnnouncement_bad_command(t *testing.T) {
Command: []string{"exit"},
Args: []string{"1"},
}
_, err := getParametersAnnouncement(context.Background(), "", []*repoclient.ParameterAnnouncement{}, command)
_, err := getParametersAnnouncement(context.Background(), "", []*repoclient.ParameterAnnouncement{}, command, []*apiclient.EnvEntry{})
assert.Error(t, err)
assert.Contains(t, err.Error(), "error executing dynamic parameter output command")
}
@@ -729,16 +730,17 @@ func TestService_GetParametersAnnouncement(t *testing.T) {
require.NoError(t, err)
t.Run("successful response", func(t *testing.T) {
s, err := NewMockParametersAnnouncementStream("./testdata/kustomize", "./testdata/kustomize", nil)
s, err := NewMockParametersAnnouncementStream("./testdata/kustomize", "./testdata/kustomize", []string{"MUST_BE_SET=yep"})
require.NoError(t, err)
err = service.GetParametersAnnouncement(s)
require.NoError(t, err)
require.NotNil(t, s.response)
require.Len(t, s.response.ParameterAnnouncements, 1)
assert.Equal(t, repoclient.ParameterAnnouncement{Name: "test-param", String_: "test-value"}, *s.response.ParameterAnnouncements[0])
require.Len(t, s.response.ParameterAnnouncements, 2)
assert.Equal(t, repoclient.ParameterAnnouncement{Name: "dynamic-test-param", String_: "yep"}, *s.response.ParameterAnnouncements[0])
assert.Equal(t, repoclient.ParameterAnnouncement{Name: "test-param", String_: "test-value"}, *s.response.ParameterAnnouncements[1])
})
t.Run("out of bounds app", func(t *testing.T) {
s, err := NewMockParametersAnnouncementStream("./testdata/kustomize", "./testdata/kustomize", nil)
s, err := NewMockParametersAnnouncementStream("./testdata/kustomize", "./testdata/kustomize", []string{"MUST_BE_SET=yep"})
require.NoError(t, err)
// set a malicious app path on the metadata
s.metadataRequest.Request.(*apiclient.AppStreamRequest_Metadata).Metadata.AppRelPath = "../out-of-bounds"
@@ -746,4 +748,38 @@ func TestService_GetParametersAnnouncement(t *testing.T) {
require.ErrorContains(t, err, "illegal appPath")
require.Nil(t, s.response)
})
t.Run("fails when script fails", func(t *testing.T) {
s, err := NewMockParametersAnnouncementStream("./testdata/kustomize", "./testdata/kustomize", []string{"WRONG_ENV_VAR=oops"})
require.NoError(t, err)
err = service.GetParametersAnnouncement(s)
require.ErrorContains(t, err, "error executing dynamic parameter output command")
require.Nil(t, s.response)
})
}
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

@@ -5,9 +5,15 @@ metadata:
spec:
version: v1.0
init:
command: [kustomize, version]
command: [sh, -c]
args:
- |
kustomize version
generate:
command: [sh, -c, "kustomize build"]
command: [sh, -c]
args:
- |
kustomize build
discover:
find:
command: [sh, -c, find . -name kustomization.yaml]
@@ -16,3 +22,12 @@ spec:
static:
- name: test-param
string: test-value
dynamic:
command: [sh, -c]
args:
- |
# Make sure env vars are making it to the plugin.
if [ -z "$MUST_BE_SET" ]; then
exit 1
fi
echo "[{\"name\": \"dynamic-test-param\", \"string\": \"$MUST_BE_SET\"}]"

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"
@@ -394,6 +395,20 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e
return nil, fmt.Errorf("error getting custom label: %w", err)
}
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),
@@ -425,7 +440,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)
@@ -473,10 +488,11 @@ func (c *liveStateCache) getSyncedCluster(server string) (clustercache.ClusterCa
func (c *liveStateCache) invalidate(cacheSettings cacheSettings) {
log.Info("invalidating live state cache")
c.lock.Lock()
defer c.lock.Unlock()
c.cacheSettings = cacheSettings
for _, clust := range c.clusters {
clusters := c.clusters
c.lock.Unlock()
for _, clust := range clusters {
clust.Invalidate(clustercache.SetSettings(cacheSettings.clusterSettings))
}
log.Info("live state cache invalidated")

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
docs/assets/extra_info.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

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

@@ -81,7 +81,7 @@ in your Argo CD installation namespace. You can simply retrieve this password
using the `argocd` CLI:
```bash
argocd admin initial-password
argocd admin initial-password -n argocd
```
!!! warning

View File

@@ -114,9 +114,7 @@ spec:
# plugin specific config
plugin:
# NOTE: this field is deprecated in v2.5 and must be removed to use sidecar-based plugins.
# Only set the plugin name if the plugin is defined in argocd-cm.
# If the plugin is defined as a sidecar, omit the name. The plugin will be automatically matched with the
# If the plugin is defined as a sidecar and name is not passed, the plugin will be automatically matched with the
# Application according to the plugin's discovery rules.
name: mypluginname
# environment variables passed to the plugin
@@ -148,7 +146,12 @@ spec:
# name: in-cluster
# The namespace will only be set for namespace-scoped resources that have not set a value for .metadata.namespace
namespace: guestbook
# Extra information to show in the Argo CD Application details tab
info:
- name: 'Example:'
value: 'https://example.com'
# Sync policy
syncPolicy:
automated: # automated sync by default retries failed attempts 5 times with following delays between attempts ( 5s, 10s, 20s, 40s, 80s ); retry controlled using `retry` field.

View File

@@ -110,7 +110,7 @@ spec:
server: https://kubernetes.default.svc
namespace: '{{path.basename}}'
```
(*The full example can be found [here](https://github.com/argoproj/argo-cd/tree/master/examples/applicationset/git-generator-directory/excludes).*)
(*The full example can be found [here](https://github.com/argoproj/argo-cd/tree/master/applicationset/examples/git-generator-directory/excludes).*)
This example excludes the `exclude-helm-guestbook` directory from the list of directories scanned for this `ApplicationSet` resource.

View File

@@ -0,0 +1,43 @@
# Post Selector all generators
The Selector allows to post-filter based on generated values using the kubernetes common labelSelector format. In the example, the list generator generates a set of two application which then filter by the key value to only select the `env` with value `staging`:
## Example: List generator + Post Selector
```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: guestbook
spec:
generators:
- list:
elements:
- cluster: engineering-dev
url: https://kubernetes.default.svc
env: staging
- cluster: engineering-prod
url: https://kubernetes.default.svc
env: prod
selector:
matchLabels:
env: staging
template:
metadata:
name: '{{cluster}}-guestbook'
spec:
project: default
source:
repoURL: https://github.com/argoproj-labs/applicationset.git
targetRevision: HEAD
path: examples/list-generator/guestbook/{{cluster}}
destination:
server: '{{url}}'
namespace: guestbook
```
The List generator + Post Selector generates a single set of parameters:
```yaml
- cluster: engineering-dev
url: https://kubernetes.default.svc
env: staging
```

View File

@@ -15,4 +15,6 @@ As of this writing there are eight generators:
- [Pull Request generator](Generators-Pull-Request.md): The Pull Request generator uses the API of an SCMaaS provider (eg GitHub) to automatically discover open pull requests within an repository.
- [Cluster Decision Resource generator](Generators-Cluster-Decision-Resource.md): The Cluster Decision Resource generator is used to interface with Kubernetes custom resources that use custom resource-specific logic to decide which set of Argo CD clusters to deploy to.
All generators can be filtered by using the [Post Selector](Generators-Post-Selector.md)
If you are new to generators, begin with the **List** and **Cluster** generators. For more advanced use cases, see the documentation for the remaining generators above.

View File

@@ -114,12 +114,13 @@ In order to prevent duplication of the custom health check for potentially multi
```yaml
resource.customizations: |
*.aws.crossplane.io/*:
"*.aws.crossplane.io/*":
health.lua: |
...
```
!!!important
Please note the required quotes in the resource customization health section, if the wildcard starts with `*`.
The `obj` is a global variable which contains the resource. The script must return an object with status and optional message field.
The custom health check might return one of the following health statuses:

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. Additionally, IPv6 only clusters are not supported.
@@ -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`.
@@ -188,4 +186,4 @@ spec:
targetRevision: HEAD
path: my-application
# ...
```
```

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/base?ref=v2.6.2
```
## Helm

View File

@@ -64,7 +64,7 @@ See [Web-based Terminal](web_based_terminal.md) for more info.
#### The `applicationsets` resource
[ApplicationSets](applicationset) provide a declarative way to automatically create/update/delete Applications.
[ApplicationSets](applicationset/index.md) provide a declarative way to automatically create/update/delete Applications.
Granting `applicationsets, create` effectively grants the ability to create Applications. While it doesn't allow the
user to create Applications directly, they can create Applications via an ApplicationSet.

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

@@ -1,5 +1,57 @@
# v2.4 to 2.5
## Known Issues
### Broken `project` filter before 2.5.15
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.
### Broken matrix-nested git files generator in 2.5.14
Argo CD 2.5.14 introduced a bug in the matrix-nested git files generator. The bug only applies when the git files
generator is the second generator nested under a matrix. For example:
```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: guestbook
spec:
generators:
- matrix:
generators:
- clusters: {}
- git:
repoURL: https://git.example.com/org/repo.git
revision: HEAD
files:
- path: "defaults/*.yaml"
template:
# ...
```
The nested git files generator will produce no parameters, causing the matrix generator to also produce no parameters.
This will cause the ApplicationSet to produce no Applications. If the ApplicationSet controller is
[configured with the ability to delete applications](https://argo-cd.readthedocs.io/en/latest/operator-manual/applicationset/Controlling-Resource-Modification/),
it will delete all Applications which were previously created by the ApplicationSet.
To avoid this issue, upgrade directly to >=2.5.15 or >= 2.6.6.
## Configure RBAC to account for new `applicationsets` resource
2.5 introduces a new `applicationsets` [RBAC resource](https://argo-cd.readthedocs.io/en/stable/operator-manual/rbac/#rbac-resources-and-actions).
@@ -132,3 +184,16 @@ This note is just for clarity. No action is required.
We [expected](../upgrading/2.3-2.4.md#enable-logs-rbac-enforcement) to enable logs RBAC enforcement by default in 2.5.
We have decided not to do that in the 2.x series due to disruption for users of [Project Roles](../../user-guide/projects.md#project-roles).
## `argocd app create` for old CLI versions fails with API version >=2.5.16
Starting with Argo CD 2.5.16, the API returns `PermissionDenied` instead of `NotFound` for Application `GET` requests if
the Application does not exist.
The Argo CD CLI before versions starting with version 2.5.0-rc1 and before versions 2.5.16 and 2.6.7 does a `GET`
request before the `POST` request in `argocd app create`. The command does not gracefully handle the `PermissionDenied`
response and will therefore fail to create/update the Application.
To solve the issue, upgrade the CLI to at least 2.5.16, or 2.6.7.
CLIs older than 2.5.0-rc1 are unaffected.

View File

@@ -1,5 +1,57 @@
# v2.5 to 2.6
## Known Issues
### Broken `project` filter before 2.6.6
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.
### Broken matrix-nested git files generator in 2.6.5
Argo CD 2.6.5 introduced a bug in the matrix-nested git files generator. The bug only applies when the git files
generator is the second generator nested under a matrix. For example:
```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: guestbook
spec:
generators:
- matrix:
generators:
- clusters: {}
- git:
repoURL: https://git.example.com/org/repo.git
revision: HEAD
files:
- path: "defaults/*.yaml"
template:
# ...
```
The nested git files generator will produce no parameters, causing the matrix generator to also produce no parameters.
This will cause the ApplicationSet to produce no Applications. If the ApplicationSet controller is
[configured with the ability to delete applications](https://argo-cd.readthedocs.io/en/latest/operator-manual/applicationset/Controlling-Resource-Modification/),
it will delete all Applications which were previously created by the ApplicationSet.
To avoid this issue, upgrade directly to >=2.5.15 or >= 2.6.6.
## ApplicationSets: `^` behavior change in Sprig's semver functions
Argo CD 2.5 introduced [Go templating in ApplicationSets](https://argo-cd.readthedocs.io/en/stable/operator-manual/applicationset/GoTemplate/). Go templates have access to the Sprig function library.
@@ -29,3 +81,17 @@ name. Argo CD v2.6 introduces support for specifying sidecar plugins by name.
Removal of argocd-cm plugin support has been delayed until 2.7 to provide a transition time for users who need to
specify plugins by name.
## `argocd app create` for old CLI versions fails with API version >=2.6.7
Starting with Argo CD 2.6.7, the API returns `PermissionDenied` instead of `NotFound` for Application `GET` requests if
the Application does not exist.
The Argo CD CLI before versions starting with version 2.5.0-rc1 and before versions 2.5.16 and 2.6.7 does a `GET`
request before the `POST` request in `argocd app create`. The command does not gracefully handle the `PermissionDenied`
response and will therefore fail to create/update the Application.
To solve the issue, upgrade the CLI to at least 2.5.16, or 2.6.7.
CLIs older than 2.5.0-rc1 are unaffected.

View File

@@ -94,6 +94,7 @@ data:
```
Make sure that:
- __issuer__ ends with the correct realm (in this example _master_)
- __issuer__ on Keycloak releases older than version 17 the URL must include /auth (in this example /auth/realms/master)
- __clientID__ is set to the Client ID you configured in Keycloak

View File

@@ -13,4 +13,4 @@ Before effectively using Argo CD, it is necessary to understand the underlying t
* [Helm](https://helm.sh)
* If you're integrating with a CI tool:
* [GitHub Actions Documentation](https://docs.github.com/en/actions)
* [Jenkins User Guide](https://jenkins.io](https://www.jenkins.io/doc/book/)
* [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

@@ -48,40 +48,4 @@ Within ApplicationSet there exist other more powerful generators in addition to
To learn more about the ApplicationSet controller, check out [ApplicationSet documentation](../operator-manual/applicationset/index.md) to install the ApplicationSet controller alongside Argo CD.
**Note:** Starting `v2.3` of Argo CD, we don't need to install ApplicationSet Controller separately. It would be instead as part of Argo CD installation.
#### Post Selector all generators
The Selector allows to post-filter based on generated values using the kubernetes common labelSelector format. In the example, the list generator generates a set of two application which then filter by the key value to only select the `env` with value `staging`:
```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: guestbook
spec:
generators:
- list:
elements:
- cluster: engineering-dev
url: https://kubernetes.default.svc
env: staging
- cluster: engineering-prod
url: https://kubernetes.default.svc
env: prod
selector:
matchLabels:
env: staging
template:
metadata:
name: '{{cluster}}-guestbook'
spec:
project: default
source:
repoURL: https://github.com/argoproj-labs/applicationset.git
targetRevision: HEAD
path: examples/list-generator/guestbook/{{cluster}}
destination:
server: '{{url}}'
namespace: guestbook
```
**Note:** Starting `v2.3` of Argo CD, we don't need to install ApplicationSet Controller separately. It would be instead as part of Argo CD installation.

View File

@@ -25,6 +25,7 @@ argocd admin dashboard [flags]
--password string Password for basic authentication to the API server
--port int Listen on given port (default 8080)
--proxy-url string If provided, this URL will be used to connect via proxy
--redis-compress string Enable this if the application controller is configured with redis compression enabled. (possible values: none, gzip) (default "none")
--request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0")
--tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used.
--token string Bearer token for authentication to the API server

View File

@@ -0,0 +1,28 @@
# Add extra Application info
You can add additional information to an Application on your ArgoCD dashboard.
If you wish to add clickable links, see [Add external URL](https://argo-cd.readthedocs.io/en/stable/user-guide/external-url/).
This is done by providing the 'info' field a key-value in your Application manifest.
Example:
```yaml
project: argo-demo
source:
repoURL: 'https://demo'
path: argo-demo
destination:
server: https://demo
namespace: argo-demo
info:
- name: Example:
value: >-
https://example.com
```
![External link](../assets/extra_info-1.png)
The additional information will be visible on the ArgoCD Application details page.
![External link](../assets/extra_info.png)
![External link](../assets/extra_info-2.png)

View File

@@ -115,7 +115,7 @@ Argo CD supports many (most?) Helm hooks by mapping the Helm annotations onto Ar
| `helm.sh/hook: test-success` | Not supported. No equivalent in Argo CD. |
| `helm.sh/hook: test-failure` | Not supported. No equivalent in Argo CD. |
| `helm.sh/hook-delete-policy` | Supported. See also `argocd.argoproj.io/hook-delete-policy`). |
| `helm.sh/hook-delete-timeout` | No supported. Never used in Helm stable |
| `helm.sh/hook-delete-timeout` | Not supported. Never used in Helm stable |
| `helm.sh/hook-weight` | Supported as equivalent to `argocd.argoproj.io/sync-wave`. |
Unsupported hooks are ignored. In Argo CD, hooks are created by using `kubectl apply`, rather than `kubectl create`. This means that if the hook is named and already exists, it will not change unless you have annotated it with `before-hook-creation`.

View File

@@ -321,7 +321,7 @@ stringData:
All the examples above talk about Git repositories, but the same principles apply to clusters as well.
With cluster-scoped clusters we can also restrict projects to only allow applications whose destinations belong to the
With project-scoped clusters we can also restrict projects to only allow applications whose destinations belong to the
same project. The default behavior allows for applications to be installed onto clusters which are not a part of the same
project, as the example below demonstrates:

View File

@@ -0,0 +1,11 @@
This folder contains example RBAC for Kubernetes to allow the Argo CD API
Server (`argocd-server`) to perform CRUD operations on `Application` CRs
in all namespaces on the cluster.
Applying the `ClusterRole` and `ClusterRoleBinding` grant the Argo CD API
server read and write permissions cluster-wide, which may not be what you
want. Handle with care.
Only apply these if you have installed Argo CD into the default namespace
`argocd`. Otherwise, you need to edit the cluster role binding to bind to
the service account in the correct namespace.

View File

@@ -0,0 +1,18 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: argocd-server-cluster-apps
app.kubernetes.io/part-of: argocd
app.kubernetes.io/component: server
name: argocd-server-cluster-apps
rules:
- apiGroups:
- "argoproj.io"
resources:
- "applications"
verbs:
- create
- delete
- update
- patch

View File

@@ -0,0 +1,16 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
app.kubernetes.io/name: argocd-server-cluster-apps
app.kubernetes.io/part-of: argocd
app.kubernetes.io/component: server
name: argocd-server-cluster-apps
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: argocd-server-cluster-apps
subjects:
- kind: ServiceAccount
name: argocd-server
namespace: argocd

24
go.mod
View File

@@ -8,8 +8,9 @@ require (
github.com/Masterminds/semver/v3 v3.2.0
github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d
github.com/alicebob/miniredis/v2 v2.23.1
github.com/argoproj/gitops-engine v0.7.1-0.20221208230615-917f5a0f16d5
github.com/argoproj/notifications-engine v0.3.1-0.20221203221941-490d98afd1d6
github.com/antonmedv/expr v1.9.0
github.com/argoproj/gitops-engine v0.7.1-0.20230512020822-b4dd8b8c3976
github.com/argoproj/notifications-engine v0.4.1-0.20230228182525-f754726f03da
github.com/argoproj/pkg v0.13.7-0.20221221191914-44694015343d
github.com/aws/aws-sdk-go v1.44.164
github.com/bombsimon/logrusr/v2 v2.0.1
@@ -73,11 +74,11 @@ require (
github.com/whilp/git-urls v0.0.0-20191001220047-6db9661140c0
github.com/xanzy/go-gitlab v0.60.0
github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
golang.org/x/net v0.4.0 // indirect
golang.org/x/crypto v0.3.0
golang.org/x/net v0.7.0 // indirect
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
golang.org/x/term v0.3.0
golang.org/x/term v0.5.0
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368
google.golang.org/grpc v1.51.0
google.golang.org/protobuf v1.28.1
@@ -106,8 +107,7 @@ require (
)
require (
github.com/Masterminds/sprig/v3 v3.2.2
github.com/antonmedv/expr v1.9.0
github.com/Masterminds/sprig/v3 v3.2.3
github.com/gosimple/slug v1.13.1
github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5
github.com/robfig/cron/v3 v3.0.1
@@ -136,8 +136,6 @@ require (
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
github.com/Microsoft/go-winio v0.4.17 // indirect
github.com/PagerDuty/go-pagerduty v1.6.0 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
@@ -181,7 +179,7 @@ require (
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-version v1.2.1 // indirect
github.com/huandu/xstrings v1.3.1 // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/itchyny/timefmt-go v0.1.4 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
@@ -217,7 +215,7 @@ require (
github.com/russross/blackfriday v1.5.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/slack-go/slack v0.10.1 // indirect
github.com/slack-go/slack v0.12.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/vmihailenco/go-tinylfu v0.2.1 // indirect
github.com/vmihailenco/msgpack/v5 v5.3.4 // indirect
@@ -232,8 +230,8 @@ require (
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
golang.org/x/exp v0.0.0-20210901193431-a062eea981d2 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/text v0.5.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
golang.org/x/tools v0.1.12 // indirect
gomodules.xyz/envconfig v1.3.1-0.20190308184047-426f31af0d45 // indirect

48
go.sum
View File

@@ -84,15 +84,10 @@ github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
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/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8=
github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
@@ -137,10 +132,10 @@ github.com/antonmedv/expr v1.9.0/go.mod h1:5qsM3oLGDND7sDmQGDXHkYfkjYMUX14qsgqmH
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/appscode/go v0.0.0-20191119085241-0887d8ec2ecc/go.mod h1:OawnOmAL4ZX3YaPdN+8HTNwBveT1jMsqP74moa9XUbE=
github.com/argoproj/gitops-engine v0.7.1-0.20221208230615-917f5a0f16d5 h1:iRpHi7X3q9G55KTaMjxKicgNnS2blFHaEfOOgsmP8lE=
github.com/argoproj/gitops-engine v0.7.1-0.20221208230615-917f5a0f16d5/go.mod h1:WpA/B7tgwfz+sdNE3LqrTrb7ArEY1FOPI2pAGI0hfPc=
github.com/argoproj/notifications-engine v0.3.1-0.20221203221941-490d98afd1d6 h1:b92Xft7MQv/SP56FW08zt5CMTE1rySH8UPDKOAgSzOM=
github.com/argoproj/notifications-engine v0.3.1-0.20221203221941-490d98afd1d6/go.mod h1:pgPU59KCsBOMhyw9amRWPoSuBmUWvx3Xsc5r0mUriLg=
github.com/argoproj/gitops-engine v0.7.1-0.20230512020822-b4dd8b8c3976 h1:8i12dOcimhwrJxUznzZR/NW4JpIL5DXZjkI3Bl3yh38=
github.com/argoproj/gitops-engine v0.7.1-0.20230512020822-b4dd8b8c3976/go.mod h1:WpA/B7tgwfz+sdNE3LqrTrb7ArEY1FOPI2pAGI0hfPc=
github.com/argoproj/notifications-engine v0.4.1-0.20230228182525-f754726f03da h1:Vf9xvHcXn4TP/nLIfWn+TaC521V9fpz/DwRP6uEeVR8=
github.com/argoproj/notifications-engine v0.4.1-0.20230228182525-f754726f03da/go.mod h1:05koR0gE/O0i5YDbidg1dpr76XitK4DJveh+dIAq6e8=
github.com/argoproj/pkg v0.13.7-0.20221221191914-44694015343d h1:7fXEKF3OQ9i1PrgieA6FLrXOL3UAKyiotomn0RHevds=
github.com/argoproj/pkg v0.13.7-0.20221221191914-44694015343d/go.mod h1:RKjj5FJ6KxtktOY49GJSG49qO6Z4lH7RnrVCaS3tf18=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
@@ -642,8 +637,8 @@ github.com/heketi/heketi v10.3.0+incompatible/go.mod h1:bB9ly3RchcQqsQ9CpyaQwvva
github.com/heketi/tests v0.0.0-20151005000721-f3775cbcefd6/go.mod h1:xGMAM8JLi7UkZt1i4FQeQy0R2T8GLUwQhOP5M1gBhy4=
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs=
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@@ -1011,8 +1006,8 @@ github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c h1:fyKiXKO1/I/B6Y2U8T7WdQGWzwehOuGIrljPtt7YTTI=
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/slack-go/slack v0.10.1 h1:BGbxa0kMsGEvLOEoZmYs8T1wWfoZXwmQFBb6FgYCXUA=
github.com/slack-go/slack v0.10.1/go.mod h1:wWL//kk0ho+FcQXcBTmEafUI5dz4qz5f4mMk8oIkioQ=
github.com/slack-go/slack v0.12.1 h1:X97b9g2hnITDtNsNe5GkGx6O2/Sz/uC20ejRZN6QxOw=
github.com/slack-go/slack v0.12.1/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
@@ -1217,7 +1212,6 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
@@ -1226,8 +1220,9 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1340,8 +1335,9 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
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=
@@ -1487,13 +1483,15 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
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=
@@ -1504,8 +1502,8 @@ 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/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
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=

View File

@@ -37,7 +37,7 @@ spec:
type: RuntimeDefault
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]
env:

View File

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

View File

@@ -23,7 +23,7 @@ spec:
serviceAccountName: argocd-redis
containers:
- name: redis
image: redis:7.0.7-alpine
image: redis:7.0.11-alpine
imagePullPolicy: Always
args:
- "--save"

View File

@@ -15557,7 +15557,7 @@ spec:
key: applicationsetcontroller.enable.progressive.syncs
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -15639,7 +15639,7 @@ spec:
- ""
- --appendonly
- "no"
image: redis:7.0.7-alpine
image: redis:7.0.11-alpine
imagePullPolicy: Always
name: redis
ports:
@@ -15821,7 +15821,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -15873,7 +15873,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -16080,7 +16080,7 @@ spec:
key: application.namespaces
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

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

View File

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

View File

@@ -1071,7 +1071,7 @@ spec:
topologyKey: kubernetes.io/hostname
initContainers:
- name: config-init
image: haproxy:2.6.2-alpine
image: haproxy:2.6.12-alpine
imagePullPolicy: IfNotPresent
resources:
{}
@@ -1089,7 +1089,7 @@ spec:
mountPath: /data
containers:
- name: haproxy
image: haproxy:2.6.2-alpine
image: haproxy:2.6.12-alpine
imagePullPolicy: IfNotPresent
securityContext:
null
@@ -1179,7 +1179,7 @@ spec:
automountServiceAccountToken: false
initContainers:
- name: config-init
image: redis:7.0.7-alpine
image: redis:7.0.11-alpine
imagePullPolicy: IfNotPresent
resources:
{}
@@ -1206,7 +1206,7 @@ spec:
containers:
- name: redis
image: redis:7.0.7-alpine
image: redis:7.0.11-alpine
imagePullPolicy: IfNotPresent
command:
- redis-server
@@ -1256,7 +1256,7 @@ spec:
- /bin/sh
- /readonly-config/trigger-failover-if-master.sh
- name: sentinel
image: redis:7.0.7-alpine
image: redis:7.0.11-alpine
imagePullPolicy: IfNotPresent
command:
- redis-sentinel
@@ -1300,7 +1300,7 @@ spec:
{}
- name: split-brain-fix
image: redis:7.0.7-alpine
image: redis:7.0.11-alpine
imagePullPolicy: IfNotPresent
command:
- sh

View File

@@ -11,14 +11,14 @@ redis-ha:
IPv6:
enabled: false
image:
tag: 2.6.2-alpine
tag: 2.6.12-alpine
containerSecurityContext: null
timeout:
server: 6m
client: 6m
checkInterval: 3s
image:
tag: 7.0.7-alpine
tag: 7.0.11-alpine
containerSecurityContext: null
sentinel:
bind: "0.0.0.0"

View File

@@ -16758,7 +16758,7 @@ spec:
key: applicationsetcontroller.enable.progressive.syncs
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -16839,7 +16839,7 @@ spec:
key: dexserver.disable.tls
name: argocd-cmd-params-cm
optional: true
image: ghcr.io/dexidp/dex:v2.35.3
image: ghcr.io/dexidp/dex:v2.36.0
imagePullPolicy: Always
name: dex
ports:
@@ -16868,7 +16868,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -16921,7 +16921,7 @@ spec:
containers:
- command:
- argocd-notifications
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -16992,7 +16992,7 @@ spec:
app.kubernetes.io/name: argocd-redis-ha-haproxy
topologyKey: kubernetes.io/hostname
containers:
- image: haproxy:2.6.2-alpine
- image: haproxy:2.6.12-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -17028,7 +17028,7 @@ spec:
- /readonly/haproxy_init.sh
command:
- sh
image: haproxy:2.6.2-alpine
image: haproxy:2.6.12-alpine
imagePullPolicy: IfNotPresent
name: config-init
securityContext:
@@ -17224,7 +17224,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -17276,7 +17276,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -17555,7 +17555,7 @@ spec:
key: server.enable.proxy.extension
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -17791,7 +17791,7 @@ spec:
key: application.namespaces
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
name: argocd-application-controller
ports:
@@ -17868,7 +17868,7 @@ spec:
- /data/conf/redis.conf
command:
- redis-server
image: redis:7.0.7-alpine
image: redis:7.0.11-alpine
imagePullPolicy: IfNotPresent
lifecycle:
preStop:
@@ -17921,7 +17921,7 @@ spec:
- /data/conf/sentinel.conf
command:
- redis-sentinel
image: redis:7.0.7-alpine
image: redis:7.0.11-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -17973,7 +17973,7 @@ spec:
value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4
- name: SENTINEL_ID_2
value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca
image: redis:7.0.7-alpine
image: redis:7.0.11-alpine
imagePullPolicy: IfNotPresent
name: split-brain-fix
resources: {}
@@ -18002,7 +18002,7 @@ spec:
value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4
- name: SENTINEL_ID_2
value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca
image: redis:7.0.7-alpine
image: redis:7.0.11-alpine
imagePullPolicy: IfNotPresent
name: config-init
securityContext:

View File

@@ -1562,7 +1562,7 @@ spec:
key: applicationsetcontroller.enable.progressive.syncs
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -1643,7 +1643,7 @@ spec:
key: dexserver.disable.tls
name: argocd-cmd-params-cm
optional: true
image: ghcr.io/dexidp/dex:v2.35.3
image: ghcr.io/dexidp/dex:v2.36.0
imagePullPolicy: Always
name: dex
ports:
@@ -1672,7 +1672,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -1725,7 +1725,7 @@ spec:
containers:
- command:
- argocd-notifications
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -1796,7 +1796,7 @@ spec:
app.kubernetes.io/name: argocd-redis-ha-haproxy
topologyKey: kubernetes.io/hostname
containers:
- image: haproxy:2.6.2-alpine
- image: haproxy:2.6.12-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -1832,7 +1832,7 @@ spec:
- /readonly/haproxy_init.sh
command:
- sh
image: haproxy:2.6.2-alpine
image: haproxy:2.6.12-alpine
imagePullPolicy: IfNotPresent
name: config-init
securityContext:
@@ -2028,7 +2028,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -2080,7 +2080,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -2359,7 +2359,7 @@ spec:
key: server.enable.proxy.extension
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2595,7 +2595,7 @@ spec:
key: application.namespaces
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
name: argocd-application-controller
ports:
@@ -2672,7 +2672,7 @@ spec:
- /data/conf/redis.conf
command:
- redis-server
image: redis:7.0.7-alpine
image: redis:7.0.11-alpine
imagePullPolicy: IfNotPresent
lifecycle:
preStop:
@@ -2725,7 +2725,7 @@ spec:
- /data/conf/sentinel.conf
command:
- redis-sentinel
image: redis:7.0.7-alpine
image: redis:7.0.11-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -2777,7 +2777,7 @@ spec:
value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4
- name: SENTINEL_ID_2
value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca
image: redis:7.0.7-alpine
image: redis:7.0.11-alpine
imagePullPolicy: IfNotPresent
name: split-brain-fix
resources: {}
@@ -2806,7 +2806,7 @@ spec:
value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4
- name: SENTINEL_ID_2
value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca
image: redis:7.0.7-alpine
image: redis:7.0.11-alpine
imagePullPolicy: IfNotPresent
name: config-init
securityContext:

View File

@@ -15877,7 +15877,7 @@ spec:
key: applicationsetcontroller.enable.progressive.syncs
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -15958,7 +15958,7 @@ spec:
key: dexserver.disable.tls
name: argocd-cmd-params-cm
optional: true
image: ghcr.io/dexidp/dex:v2.35.3
image: ghcr.io/dexidp/dex:v2.36.0
imagePullPolicy: Always
name: dex
ports:
@@ -15987,7 +15987,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -16040,7 +16040,7 @@ spec:
containers:
- command:
- argocd-notifications
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -16117,7 +16117,7 @@ spec:
- ""
- --appendonly
- "no"
image: redis:7.0.7-alpine
image: redis:7.0.11-alpine
imagePullPolicy: Always
name: redis
ports:
@@ -16299,7 +16299,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -16351,7 +16351,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -16626,7 +16626,7 @@ spec:
key: server.enable.proxy.extension
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -16860,7 +16860,7 @@ spec:
key: application.namespaces
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -681,7 +681,7 @@ spec:
key: applicationsetcontroller.enable.progressive.syncs
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -762,7 +762,7 @@ spec:
key: dexserver.disable.tls
name: argocd-cmd-params-cm
optional: true
image: ghcr.io/dexidp/dex:v2.35.3
image: ghcr.io/dexidp/dex:v2.36.0
imagePullPolicy: Always
name: dex
ports:
@@ -791,7 +791,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -844,7 +844,7 @@ spec:
containers:
- command:
- argocd-notifications
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -921,7 +921,7 @@ spec:
- ""
- --appendonly
- "no"
image: redis:7.0.7-alpine
image: redis:7.0.11-alpine
imagePullPolicy: Always
name: redis
ports:
@@ -1103,7 +1103,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -1155,7 +1155,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -1430,7 +1430,7 @@ spec:
key: server.enable.proxy.extension
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -1664,7 +1664,7 @@ spec:
key: application.namespaces
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.6.3
image: quay.io/argoproj/argocd:v2.6.8
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -98,6 +98,7 @@ nav:
- operator-manual/applicationset/Generators-SCM-Provider.md
- operator-manual/applicationset/Generators-Cluster-Decision-Resource.md
- operator-manual/applicationset/Generators-Pull-Request.md
- operator-manual/applicationset/Generators-Post-Selector.md
- Template fields:
- operator-manual/applicationset/Template.md
- operator-manual/applicationset/GoTemplate.md
@@ -160,6 +161,7 @@ nav:
- user-guide/best_practices.md
- user-guide/status-badge.md
- user-guide/external-url.md
- user-guide/extra_info.md
- Notification subscriptions: user-guide/subscriptions.md
- Command Reference: user-guide/commands/argocd.md
- Developer Guide:

View File

@@ -51,7 +51,9 @@ type ApplicationQuery struct {
// the repoURL to restrict returned list applications
Repo *string `protobuf:"bytes,6,opt,name=repo" json:"repo,omitempty"`
// the application's namespace
AppNamespace *string `protobuf:"bytes,7,opt,name=appNamespace" json:"appNamespace,omitempty"`
AppNamespace *string `protobuf:"bytes,7,opt,name=appNamespace" json:"appNamespace,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:"-"`
@@ -139,6 +141,13 @@ func (m *ApplicationQuery) GetAppNamespace() 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"`
@@ -2627,169 +2636,169 @@ func init() {
}
var fileDescriptor_df6e82b174b5eaec = []byte{
// 2581 bytes of a gzipped FileDescriptorProto
// 2590 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0xcd, 0x8f, 0x1c, 0x47,
0x15, 0xa7, 0x66, 0xbf, 0x66, 0xde, 0xac, 0xbf, 0x2a, 0xf1, 0xd2, 0x69, 0xaf, 0xcd, 0xba, 0xfd,
0xb5, 0x5e, 0x7b, 0x67, 0xec, 0xc1, 0x20, 0x67, 0x13, 0x04, 0xb6, 0xe3, 0x2f, 0x58, 0x3b, 0xa6,
0xd7, 0xc6, 0x28, 0x1c, 0xa0, 0xd2, 0x53, 0x3b, 0xdb, 0x6c, 0x4f, 0x77, 0xbb, 0xbb, 0x67, 0xac,
0x91, 0xf1, 0x25, 0x88, 0x13, 0x51, 0x90, 0x92, 0x1c, 0x50, 0x14, 0x21, 0x94, 0x28, 0x17, 0x2e,
0xdc, 0x10, 0x12, 0x17, 0xb8, 0x20, 0x90, 0x38, 0x20, 0x3e, 0x2e, 0x39, 0x21, 0x8b, 0x1b, 0x17,
0xfe, 0x04, 0x54, 0xd5, 0x55, 0xdd, 0xd5, 0x33, 0x3d, 0x3d, 0xbd, 0xec, 0x46, 0xf1, 0xad, 0x5e,
0x4d, 0xd5, 0x7b, 0xbf, 0x7a, 0xf5, 0xbe, 0xea, 0xf5, 0xc0, 0xc9, 0x90, 0x06, 0x7d, 0x1a, 0x34,
0x89, 0xef, 0x3b, 0xb6, 0x45, 0x22, 0xdb, 0x73, 0xd5, 0x71, 0xc3, 0x0f, 0xbc, 0xc8, 0xc3, 0x75,
0x65, 0x4a, 0x5f, 0xec, 0x78, 0x5e, 0xc7, 0xa1, 0x4d, 0xe2, 0xdb, 0x4d, 0xe2, 0xba, 0x5e, 0xc4,
0xa7, 0xc3, 0x78, 0xa9, 0x6e, 0x6c, 0x5f, 0x0e, 0x1b, 0xb6, 0xc7, 0x7f, 0xb5, 0xbc, 0x80, 0x36,
0xfb, 0x17, 0x9b, 0x1d, 0xea, 0xd2, 0x80, 0x44, 0xb4, 0x2d, 0xd6, 0x5c, 0x4a, 0xd7, 0x74, 0x89,
0xb5, 0x65, 0xbb, 0x34, 0x18, 0x34, 0xfd, 0xed, 0x0e, 0x9b, 0x08, 0x9b, 0x5d, 0x1a, 0x91, 0xbc,
0x5d, 0xeb, 0x1d, 0x3b, 0xda, 0xea, 0xbd, 0xd9, 0xb0, 0xbc, 0x6e, 0x93, 0x04, 0x1d, 0xcf, 0x0f,
0xbc, 0x1f, 0xf2, 0xc1, 0xaa, 0xd5, 0x6e, 0xf6, 0x5b, 0x29, 0x03, 0xf5, 0x2c, 0xfd, 0x8b, 0xc4,
0xf1, 0xb7, 0xc8, 0x28, 0xb7, 0xeb, 0x13, 0xb8, 0x05, 0xd4, 0xf7, 0x84, 0x6e, 0xf8, 0xd0, 0x8e,
0xbc, 0x60, 0xa0, 0x0c, 0x63, 0x36, 0xc6, 0xa7, 0x08, 0x0e, 0x5e, 0x49, 0xe5, 0x7d, 0xbb, 0x47,
0x83, 0x01, 0xc6, 0x30, 0xed, 0x92, 0x2e, 0xd5, 0xd0, 0x12, 0x5a, 0xae, 0x99, 0x7c, 0x8c, 0x35,
0x98, 0x0b, 0xe8, 0x66, 0x40, 0xc3, 0x2d, 0xad, 0xc2, 0xa7, 0x25, 0x89, 0x75, 0xa8, 0x32, 0xe1,
0xd4, 0x8a, 0x42, 0x6d, 0x6a, 0x69, 0x6a, 0xb9, 0x66, 0x26, 0x34, 0x5e, 0x86, 0x03, 0x01, 0x0d,
0xbd, 0x5e, 0x60, 0xd1, 0xef, 0xd0, 0x20, 0xb4, 0x3d, 0x57, 0x9b, 0xe6, 0xbb, 0x87, 0xa7, 0x19,
0x97, 0x90, 0x3a, 0xd4, 0x8a, 0xbc, 0x40, 0x9b, 0xe1, 0x4b, 0x12, 0x9a, 0xe1, 0x61, 0xc0, 0xb5,
0xd9, 0x18, 0x0f, 0x1b, 0x63, 0x03, 0xe6, 0x89, 0xef, 0xdf, 0x25, 0x5d, 0x1a, 0xfa, 0xc4, 0xa2,
0xda, 0x1c, 0xff, 0x2d, 0x33, 0x67, 0x5c, 0x83, 0xda, 0x5d, 0xaf, 0x4d, 0xc7, 0x1f, 0x6a, 0x98,
0x49, 0x25, 0x87, 0xc9, 0x36, 0x1c, 0x36, 0x69, 0xdf, 0x66, 0x20, 0xef, 0xd0, 0x88, 0xb4, 0x49,
0x44, 0x86, 0x19, 0x56, 0x12, 0x86, 0x3a, 0x54, 0x03, 0xb1, 0x58, 0xab, 0xf0, 0xf9, 0x84, 0x1e,
0x11, 0x36, 0x95, 0x23, 0xec, 0x2f, 0x08, 0x8e, 0x29, 0xd7, 0x61, 0x0a, 0x25, 0x5d, 0xef, 0x53,
0x37, 0x0a, 0xc7, 0x8b, 0x3d, 0x0f, 0x87, 0xa4, 0x3e, 0x87, 0x0f, 0x33, 0xfa, 0x03, 0x03, 0xa2,
0x4e, 0x4a, 0x20, 0xea, 0x1c, 0x5e, 0x82, 0xba, 0xa4, 0x1f, 0xdc, 0x7e, 0x4d, 0x5c, 0x9a, 0x3a,
0x35, 0x72, 0x9c, 0x99, 0x9c, 0xe3, 0xb8, 0xa0, 0x29, 0xa7, 0xb9, 0x43, 0x5c, 0x7b, 0x93, 0x86,
0x51, 0x59, 0xf5, 0xa1, 0x1d, 0xab, 0xef, 0x38, 0xd4, 0x6e, 0xd8, 0x0e, 0xbd, 0xb6, 0xd5, 0x73,
0xb7, 0xf1, 0x8b, 0x30, 0x63, 0xb1, 0x01, 0x97, 0x30, 0x6f, 0xc6, 0x84, 0xf1, 0x18, 0x8e, 0x8f,
0x83, 0xf4, 0xd0, 0x8e, 0xb6, 0xd8, 0xf6, 0x70, 0x1c, 0x36, 0x6b, 0x8b, 0x5a, 0xdb, 0x61, 0xaf,
0x2b, 0xaf, 0x56, 0xd2, 0xa5, 0xb0, 0xfd, 0x0a, 0xc1, 0xf2, 0x44, 0xc9, 0x0f, 0x03, 0xe2, 0xfb,
0x34, 0xc0, 0x37, 0x60, 0xe6, 0x11, 0xfb, 0x81, 0x5b, 0x6b, 0xbd, 0xd5, 0x68, 0xa8, 0x31, 0x6d,
0x22, 0x97, 0x5b, 0x5f, 0x30, 0xe3, 0xed, 0xb8, 0x21, 0x75, 0x50, 0xe1, 0x7c, 0x16, 0x32, 0x7c,
0x12, 0x55, 0xb1, 0xf5, 0x7c, 0xd9, 0xd5, 0x59, 0x98, 0xf6, 0x49, 0x10, 0x19, 0x87, 0xe1, 0x85,
0xac, 0x19, 0xfa, 0x9e, 0x1b, 0x52, 0xe3, 0x77, 0x28, 0x73, 0xa1, 0xd7, 0x02, 0x4a, 0x22, 0x6a,
0xd2, 0x47, 0x3d, 0x1a, 0x46, 0x78, 0x1b, 0xd4, 0x30, 0xcb, 0x75, 0x57, 0x6f, 0xdd, 0x6e, 0xa4,
0x71, 0xaa, 0x21, 0xe3, 0x14, 0x1f, 0x7c, 0xdf, 0x6a, 0x37, 0xfa, 0xad, 0x86, 0xbf, 0xdd, 0x69,
0xb0, 0xa8, 0x97, 0x41, 0x26, 0xa3, 0x9e, 0x7a, 0x54, 0x53, 0xe5, 0x8e, 0x17, 0x60, 0xb6, 0xe7,
0x87, 0x34, 0x88, 0xf8, 0xc9, 0xaa, 0xa6, 0xa0, 0xd8, 0x2d, 0xf5, 0x89, 0x63, 0xb7, 0x49, 0x14,
0xdf, 0x42, 0xd5, 0x4c, 0x68, 0xe3, 0xe3, 0x2c, 0xfa, 0x07, 0x7e, 0xfb, 0xf3, 0x42, 0xaf, 0xa2,
0xac, 0x0c, 0xa1, 0xfc, 0x20, 0x8b, 0xf2, 0x35, 0xea, 0xd0, 0x14, 0x65, 0x9e, 0x61, 0x6a, 0x30,
0x67, 0x91, 0xd0, 0x22, 0x6d, 0xc9, 0x4b, 0x92, 0x2c, 0x2c, 0xf8, 0x81, 0xe7, 0x93, 0x0e, 0xe7,
0x74, 0xcf, 0x73, 0x6c, 0x6b, 0x20, 0x6c, 0x73, 0xf4, 0x87, 0x11, 0x23, 0x9e, 0xce, 0x31, 0xe2,
0x13, 0x50, 0xdf, 0x18, 0xb8, 0xd6, 0xeb, 0x3e, 0x4f, 0x99, 0xcc, 0xc5, 0xec, 0x88, 0x76, 0x43,
0x0d, 0xf1, 0xb8, 0x1f, 0x13, 0xc6, 0x87, 0x33, 0xb0, 0xa0, 0x9c, 0x80, 0x6d, 0x28, 0xc2, 0x5f,
0xe4, 0xf4, 0x0b, 0x30, 0xdb, 0x0e, 0x06, 0x66, 0xcf, 0x15, 0x97, 0x29, 0x28, 0x26, 0xd8, 0x0f,
0x7a, 0x6e, 0x0c, 0xb2, 0x6a, 0xc6, 0x04, 0xde, 0x84, 0x6a, 0x18, 0xb1, 0x24, 0xd9, 0x19, 0xf0,
0x70, 0x54, 0x6f, 0x7d, 0x73, 0x77, 0x17, 0xc8, 0xa0, 0x6f, 0x08, 0x8e, 0x66, 0xc2, 0x1b, 0x3f,
0x82, 0x9a, 0x8c, 0x84, 0xa1, 0x36, 0xb7, 0x34, 0xb5, 0x5c, 0x6f, 0x6d, 0xec, 0x5e, 0xd0, 0xeb,
0x3e, 0x4b, 0xf0, 0x4a, 0xd4, 0x37, 0x53, 0x29, 0x78, 0x11, 0x6a, 0x5d, 0xe1, 0xeb, 0xa1, 0x56,
0xe5, 0xda, 0x4e, 0x27, 0xf0, 0x77, 0x61, 0xc6, 0x76, 0x37, 0xbd, 0x50, 0xab, 0x71, 0x30, 0x57,
0x77, 0x07, 0xe6, 0xb6, 0xbb, 0xe9, 0x99, 0x31, 0x43, 0xfc, 0x08, 0xf6, 0x05, 0x34, 0x0a, 0x06,
0x52, 0x0b, 0x1a, 0x70, 0xbd, 0x7e, 0x6b, 0x77, 0x12, 0x4c, 0x95, 0xa5, 0x99, 0x95, 0x80, 0xd7,
0xa0, 0x1e, 0xa6, 0x36, 0xa6, 0xd5, 0xb9, 0x40, 0x2d, 0xc3, 0x48, 0xb1, 0x41, 0x53, 0x5d, 0x3c,
0x62, 0xc3, 0xf3, 0x39, 0x36, 0xfc, 0x4f, 0x04, 0x8b, 0x23, 0x61, 0x60, 0xc3, 0xa7, 0x85, 0x46,
0x4a, 0x60, 0x3a, 0xf4, 0xa9, 0xc5, 0x23, 0x7f, 0xbd, 0x75, 0x67, 0xcf, 0xe2, 0x02, 0x97, 0xcb,
0x59, 0x17, 0x85, 0xae, 0x52, 0xbe, 0xf9, 0x13, 0x04, 0x5f, 0x54, 0x38, 0xdf, 0x23, 0x91, 0xb5,
0x55, 0x74, 0x24, 0xe6, 0x43, 0x6c, 0x8d, 0xc8, 0x66, 0x31, 0xc1, 0x0c, 0x8d, 0x0f, 0xee, 0x0f,
0x7c, 0x06, 0x83, 0xfd, 0x92, 0x4e, 0x94, 0x4a, 0xfa, 0xef, 0x22, 0xd0, 0xd5, 0xc8, 0xe7, 0x39,
0xce, 0x9b, 0xc4, 0xda, 0x2e, 0x82, 0xb2, 0x1f, 0x2a, 0x76, 0x9b, 0xe3, 0x98, 0x32, 0x2b, 0x76,
0x7b, 0x87, 0x6e, 0x3f, 0x0c, 0x6a, 0x36, 0x07, 0xd4, 0xa7, 0x43, 0xa0, 0xa4, 0x8b, 0x15, 0x80,
0x5a, 0x84, 0x9a, 0x3b, 0x54, 0x4c, 0xa5, 0x13, 0x39, 0x45, 0x54, 0x65, 0xa4, 0x88, 0xd2, 0x60,
0xae, 0x9f, 0x54, 0xbd, 0xec, 0x67, 0x49, 0xb2, 0x83, 0x74, 0x02, 0xaf, 0xe7, 0x0b, 0x05, 0xc6,
0x04, 0x43, 0xb1, 0x6d, 0xbb, 0x6d, 0x6d, 0x36, 0x46, 0xc1, 0xc6, 0xa5, 0xea, 0xdc, 0xf7, 0x2a,
0xf0, 0xa5, 0x9c, 0xc3, 0x4d, 0xb4, 0x80, 0xe7, 0xe3, 0x84, 0x89, 0x1d, 0xce, 0x8d, 0xb5, 0xc3,
0xea, 0x24, 0x3b, 0xac, 0xe5, 0x68, 0xe5, 0x9d, 0x0a, 0x2c, 0xe5, 0x68, 0x65, 0x72, 0x42, 0x7d,
0x6e, 0xd4, 0xb2, 0xe9, 0x05, 0xe2, 0xc6, 0xab, 0x66, 0x4c, 0x30, 0xcf, 0xf0, 0x02, 0x7f, 0x8b,
0xb8, 0x5a, 0x35, 0xf6, 0x8c, 0x98, 0x2a, 0xa5, 0x90, 0xff, 0x22, 0xd0, 0xa4, 0x16, 0xae, 0x58,
0x5c, 0x27, 0x3d, 0xf7, 0xf9, 0x57, 0xc4, 0x02, 0xcc, 0x12, 0x8e, 0x56, 0x18, 0x88, 0xa0, 0x46,
0x8e, 0x5c, 0xcd, 0x8f, 0x89, 0x47, 0xb2, 0x47, 0x0e, 0xd7, 0xed, 0x30, 0x92, 0x05, 0x2d, 0xde,
0x84, 0xb9, 0x98, 0x5b, 0x5c, 0xc2, 0xd4, 0x5b, 0xeb, 0xbb, 0x4d, 0x6c, 0x19, 0xf5, 0x4a, 0xe6,
0xc6, 0xcb, 0x70, 0x24, 0x37, 0xfa, 0x08, 0x18, 0x3a, 0x54, 0x65, 0x32, 0x17, 0x17, 0x90, 0xd0,
0xc6, 0x7f, 0xa6, 0xb2, 0x61, 0xdd, 0x6b, 0xaf, 0x7b, 0x9d, 0x82, 0xb7, 0x60, 0xf1, 0xa5, 0x69,
0x30, 0xe7, 0x7b, 0x6d, 0xe5, 0xd9, 0x27, 0x49, 0xb6, 0xcf, 0xf2, 0xdc, 0x88, 0xd8, 0x2e, 0x0d,
0x44, 0x7e, 0x49, 0x27, 0x98, 0xb2, 0x43, 0xdb, 0xb5, 0xe8, 0x06, 0xb5, 0x3c, 0xb7, 0x1d, 0xf2,
0x5b, 0x9b, 0x32, 0x33, 0x73, 0xf8, 0x16, 0xd4, 0x38, 0x7d, 0xdf, 0xee, 0xc6, 0x41, 0xb8, 0xde,
0x5a, 0x69, 0xc4, 0xad, 0x92, 0x86, 0xda, 0x2a, 0x49, 0x75, 0xd8, 0xa5, 0x11, 0x69, 0xf4, 0x2f,
0x36, 0xd8, 0x0e, 0x33, 0xdd, 0xcc, 0xb0, 0x44, 0xc4, 0x76, 0xd6, 0x6d, 0x97, 0x17, 0x58, 0x4c,
0x54, 0x3a, 0xc1, 0x0c, 0x62, 0xd3, 0x73, 0x1c, 0xef, 0xb1, 0xf4, 0x81, 0x98, 0x62, 0xbb, 0x7a,
0x6e, 0x64, 0x3b, 0x5c, 0x7e, 0xec, 0x00, 0xe9, 0x04, 0xdf, 0x65, 0x3b, 0x11, 0x0d, 0x78, 0x09,
0x53, 0x33, 0x05, 0x95, 0x98, 0x5c, 0x3d, 0xee, 0x0b, 0x48, 0xdf, 0x8b, 0x8d, 0x73, 0x5e, 0x35,
0xce, 0x61, 0x83, 0xdf, 0x97, 0xf3, 0x6e, 0xe6, 0xcd, 0x10, 0xda, 0xb7, 0xbd, 0x5e, 0xa8, 0xed,
0x8f, 0x93, 0xb8, 0xa4, 0x47, 0x0c, 0xf6, 0x40, 0x8e, 0xc1, 0xfe, 0x1e, 0x41, 0x75, 0xdd, 0xeb,
0x5c, 0x77, 0xa3, 0x60, 0xc0, 0x2b, 0x7b, 0xcf, 0x8d, 0xa8, 0x2b, 0xad, 0x42, 0x92, 0x4c, 0xd5,
0x91, 0xdd, 0xa5, 0x1b, 0x11, 0xe9, 0xfa, 0xa2, 0x26, 0xd9, 0x91, 0xaa, 0x93, 0xcd, 0xec, 0xf8,
0x0e, 0x09, 0x23, 0xee, 0xbd, 0x55, 0x93, 0x8f, 0x19, 0xd0, 0x64, 0xc1, 0x46, 0x14, 0x08, 0xd7,
0xcd, 0xcc, 0xa9, 0x86, 0x34, 0x13, 0x63, 0x13, 0xa4, 0xb1, 0x01, 0x2f, 0x25, 0xa5, 0xec, 0x7d,
0x1a, 0x74, 0x6d, 0x97, 0x14, 0xc7, 0xdb, 0x32, 0x5d, 0x98, 0x07, 0x19, 0x07, 0x62, 0xf5, 0xdf,
0x43, 0xdb, 0x6d, 0x7b, 0x8f, 0x0b, 0x1c, 0xa1, 0x0c, 0xdb, 0xbf, 0x65, 0xfb, 0x2d, 0x0a, 0xdf,
0xc4, 0x37, 0x6f, 0xc1, 0x3e, 0xe6, 0xc5, 0x7d, 0x2a, 0x7e, 0x10, 0x81, 0xc2, 0x18, 0xf7, 0x24,
0x4f, 0x79, 0x98, 0xd9, 0x8d, 0x78, 0x1d, 0x0e, 0x90, 0x30, 0xb4, 0x3b, 0x2e, 0x6d, 0x4b, 0x5e,
0x95, 0xd2, 0xbc, 0x86, 0xb7, 0xc6, 0xcf, 0x3e, 0xbe, 0x42, 0xdc, 0x9d, 0x24, 0x8d, 0x1f, 0x23,
0x38, 0x9c, 0xcb, 0x24, 0xb1, 0x75, 0xa4, 0x84, 0x57, 0x1d, 0xaa, 0xa1, 0xb5, 0x45, 0xdb, 0x3d,
0x87, 0xca, 0xbe, 0x86, 0xa4, 0xd9, 0x6f, 0xed, 0x5e, 0x7c, 0x93, 0x22, 0xbc, 0x27, 0x34, 0x3e,
0x06, 0xd0, 0x25, 0x6e, 0x8f, 0x38, 0x1c, 0xc2, 0x34, 0x87, 0xa0, 0xcc, 0x18, 0x8b, 0xa0, 0xe7,
0x99, 0x81, 0xe8, 0x24, 0xfc, 0x03, 0xc1, 0x7e, 0x19, 0x06, 0xc5, 0x1d, 0x2e, 0xc3, 0x01, 0x45,
0x0d, 0x77, 0xd3, 0xeb, 0x1c, 0x9e, 0x9e, 0x10, 0xe2, 0xa4, 0x2d, 0x4c, 0x65, 0xbb, 0x97, 0xfd,
0x4c, 0xff, 0xb1, 0x74, 0x1e, 0x42, 0x3b, 0xaa, 0xc4, 0x7e, 0x04, 0xda, 0x1d, 0xe2, 0x92, 0x0e,
0x6d, 0x27, 0x87, 0x4b, 0x0c, 0xe9, 0x07, 0xea, 0x63, 0x79, 0xd7, 0x4f, 0xd3, 0xa4, 0x9c, 0xb1,
0x37, 0x37, 0xe5, 0xc3, 0x3b, 0x80, 0xea, 0xba, 0xed, 0x6e, 0xb3, 0xf7, 0x1b, 0x3b, 0x57, 0x64,
0x47, 0x8e, 0xd4, 0x61, 0x4c, 0xe0, 0x83, 0x30, 0xd5, 0x0b, 0x1c, 0x71, 0xcf, 0x6c, 0x88, 0x97,
0xa0, 0xde, 0xa6, 0xa1, 0x15, 0xd8, 0xbe, 0xb8, 0x65, 0xde, 0xe8, 0x53, 0xa6, 0x98, 0xb6, 0x6d,
0xcb, 0x73, 0xaf, 0x39, 0x24, 0x0c, 0x65, 0x62, 0x48, 0x26, 0x8c, 0x57, 0x61, 0x1f, 0x93, 0x99,
0x1e, 0xf3, 0x5c, 0xf6, 0x98, 0x87, 0x33, 0xf0, 0x25, 0x3c, 0x89, 0xf8, 0x26, 0xbc, 0xc0, 0xf2,
0xf1, 0x15, 0xdf, 0x17, 0x4c, 0x4a, 0x16, 0x23, 0x53, 0x43, 0x97, 0xde, 0xfa, 0xa9, 0x01, 0x58,
0xb5, 0x79, 0x1a, 0xf4, 0x6d, 0x8b, 0xe2, 0x77, 0x11, 0x4c, 0x33, 0x01, 0xf8, 0xe8, 0x38, 0x17,
0xe3, 0xb6, 0xa7, 0xef, 0xdd, 0x83, 0x8e, 0x49, 0x33, 0x16, 0xdf, 0xfa, 0xfb, 0xbf, 0xdf, 0xab,
0x2c, 0xe0, 0x17, 0xf9, 0x67, 0x84, 0xfe, 0x45, 0xb5, 0xa5, 0x1f, 0xe2, 0xb7, 0x11, 0x60, 0x51,
0x85, 0x28, 0xdd, 0x5d, 0x7c, 0x6e, 0x1c, 0xc4, 0x9c, 0x2e, 0xb0, 0x7e, 0x54, 0x89, 0xf6, 0x0d,
0xcb, 0x0b, 0x28, 0x8b, 0xed, 0x7c, 0x01, 0x07, 0xb0, 0xc2, 0x01, 0x9c, 0xc4, 0x46, 0x1e, 0x80,
0xe6, 0x13, 0xa6, 0xb7, 0xa7, 0x4d, 0x1a, 0xcb, 0xfd, 0x08, 0xc1, 0xcc, 0x43, 0x5e, 0x73, 0x4f,
0x50, 0xd2, 0xc6, 0x9e, 0x29, 0x89, 0x8b, 0xe3, 0x68, 0x8d, 0x13, 0x1c, 0xe9, 0x51, 0x7c, 0x44,
0x22, 0x0d, 0xa3, 0x80, 0x92, 0x6e, 0x06, 0xf0, 0x05, 0x84, 0x3f, 0x41, 0x30, 0x1b, 0xb7, 0x1b,
0xf1, 0xa9, 0x71, 0x28, 0x33, 0xed, 0x48, 0x7d, 0xef, 0x7a, 0x77, 0xc6, 0x59, 0x8e, 0xf1, 0x84,
0x91, 0x7b, 0x9d, 0x6b, 0x99, 0xce, 0xde, 0xfb, 0x08, 0xa6, 0x6e, 0xd2, 0x89, 0xf6, 0xb6, 0x87,
0xe0, 0x46, 0x14, 0x98, 0x73, 0xd5, 0xf8, 0x63, 0x04, 0x2f, 0xdd, 0xa4, 0x51, 0x7e, 0xaa, 0xc3,
0xcb, 0x93, 0xf3, 0x8f, 0x30, 0xbb, 0x73, 0x25, 0x56, 0x26, 0x31, 0xbe, 0xc9, 0x91, 0x9d, 0xc5,
0x67, 0x8a, 0x8c, 0x30, 0x1c, 0xb8, 0xd6, 0x63, 0x81, 0xe3, 0xcf, 0x08, 0x0e, 0x0e, 0x7f, 0x6b,
0xc1, 0xd9, 0xe4, 0x98, 0xfb, 0x29, 0x46, 0xbf, 0xbb, 0xdb, 0x58, 0x9a, 0x65, 0x6a, 0x5c, 0xe1,
0xc8, 0x5f, 0xc1, 0x2f, 0x17, 0x21, 0x97, 0x4d, 0xca, 0xb0, 0xf9, 0x44, 0x0e, 0x9f, 0xf2, 0x8f,
0x7f, 0x1c, 0xf6, 0x5b, 0x08, 0xe6, 0x6f, 0xd2, 0xe8, 0x4e, 0xd2, 0xa3, 0x3b, 0x55, 0xaa, 0x87,
0xaf, 0x2f, 0x36, 0x94, 0x6f, 0x74, 0xf2, 0xa7, 0x44, 0xa5, 0xab, 0x1c, 0xd8, 0x19, 0x7c, 0xaa,
0x08, 0x58, 0xda, 0x17, 0xfc, 0x08, 0xc1, 0x61, 0x15, 0x44, 0xfa, 0x85, 0xe3, 0x2b, 0x3b, 0xfb,
0xa2, 0x20, 0xbe, 0x4b, 0x4c, 0x40, 0xd7, 0xe2, 0xe8, 0xce, 0x1b, 0xf9, 0x17, 0xde, 0x1d, 0x41,
0xb1, 0x86, 0x56, 0x96, 0x11, 0xfe, 0x03, 0x82, 0xd9, 0xb8, 0x09, 0x37, 0x5e, 0x47, 0x99, 0x5e,
0xfd, 0x5e, 0x7a, 0xcf, 0x75, 0x0e, 0xf9, 0xeb, 0xfa, 0x85, 0x7c, 0x85, 0xaa, 0xfb, 0xe5, 0xd5,
0x36, 0xb8, 0x96, 0xb3, 0x6e, 0xff, 0x1b, 0x04, 0x90, 0x36, 0x12, 0xf1, 0xd9, 0xe2, 0x73, 0x28,
0xcd, 0x46, 0x7d, 0x6f, 0x5b, 0x89, 0x46, 0x83, 0x9f, 0x67, 0x59, 0x5f, 0x2a, 0xf4, 0x39, 0x9f,
0x5a, 0x6b, 0x71, 0xd3, 0xf1, 0x97, 0x08, 0x66, 0x78, 0x9f, 0x08, 0x9f, 0x1c, 0x87, 0x59, 0x6d,
0x23, 0xed, 0xa5, 0xea, 0x4f, 0x73, 0xa8, 0x4b, 0xad, 0xa2, 0xc0, 0xb5, 0x86, 0x56, 0x70, 0x1f,
0x66, 0xe3, 0x9e, 0xcd, 0x78, 0xf3, 0xc8, 0xf4, 0x74, 0xf4, 0xa5, 0x82, 0x44, 0x1a, 0x1b, 0xaa,
0x88, 0x99, 0x2b, 0x93, 0x62, 0xe6, 0x34, 0x0b, 0x6b, 0xf8, 0x44, 0x51, 0xd0, 0xfb, 0x0c, 0x14,
0x73, 0x8e, 0xa3, 0x3b, 0x65, 0x2c, 0x4d, 0x8a, 0x9b, 0x4c, 0x3b, 0x3f, 0x47, 0x70, 0x70, 0xb8,
0xe4, 0xc4, 0x47, 0x86, 0x62, 0xa6, 0x5a, 0x67, 0xeb, 0x59, 0x2d, 0x8e, 0x2b, 0x57, 0x8d, 0x6f,
0x70, 0x14, 0x6b, 0xf8, 0xf2, 0x44, 0xcf, 0xb8, 0x2b, 0xa3, 0x0e, 0x63, 0xb4, 0x9a, 0x7e, 0xb3,
0xf8, 0x2d, 0x82, 0x79, 0xc9, 0xf7, 0x7e, 0x40, 0x69, 0x31, 0xac, 0xbd, 0x73, 0x04, 0x26, 0xcb,
0x78, 0x95, 0xc3, 0xff, 0x2a, 0xbe, 0x54, 0x12, 0xbe, 0x84, 0xbd, 0x1a, 0x31, 0xa4, 0x7f, 0x44,
0x70, 0xe8, 0x61, 0x6c, 0xf7, 0x9f, 0x13, 0xfe, 0x6b, 0x1c, 0xff, 0xd7, 0xf0, 0x2b, 0x05, 0x75,
0xd1, 0xa4, 0x63, 0x5c, 0x40, 0xf8, 0xd7, 0x08, 0xaa, 0xb2, 0x03, 0x8f, 0xcf, 0x8c, 0x75, 0x8c,
0x6c, 0x8f, 0x7e, 0x2f, 0x8d, 0x59, 0x14, 0x01, 0xc6, 0xc9, 0xc2, 0x54, 0x2a, 0xe4, 0x33, 0x83,
0x7e, 0x1f, 0x01, 0x4e, 0xde, 0x8b, 0xc9, 0x0b, 0x12, 0x9f, 0xce, 0x88, 0x1a, 0xdb, 0x60, 0xd0,
0xcf, 0x4c, 0x5c, 0x97, 0x4d, 0xa5, 0x2b, 0x85, 0xa9, 0xd4, 0x4b, 0xe4, 0xbf, 0x83, 0xa0, 0x7e,
0x93, 0x26, 0x35, 0x7b, 0x81, 0x2e, 0xb3, 0x9f, 0x16, 0xf4, 0xe5, 0xc9, 0x0b, 0x05, 0xa2, 0xf3,
0x1c, 0xd1, 0x69, 0x5c, 0xac, 0x2a, 0x09, 0xe0, 0x43, 0x04, 0xfb, 0xee, 0xa9, 0x26, 0x8a, 0xcf,
0x4f, 0x92, 0x94, 0x89, 0xe4, 0xe5, 0x71, 0x7d, 0x99, 0xe3, 0x5a, 0x35, 0x4a, 0xe1, 0x5a, 0x13,
0xfd, 0xfb, 0x5f, 0xa0, 0xf8, 0x69, 0x37, 0xd4, 0x7d, 0xfd, 0x7f, 0xf5, 0x56, 0xd0, 0xc4, 0x35,
0x2e, 0x71, 0x7c, 0x0d, 0x7c, 0xbe, 0x0c, 0xbe, 0xa6, 0x68, 0xc9, 0xe2, 0x0f, 0x10, 0x1c, 0xe2,
0xfd, 0x6f, 0x95, 0xf1, 0x50, 0x8a, 0x19, 0xd7, 0x2d, 0x2f, 0x91, 0x62, 0x44, 0xfc, 0x31, 0x76,
0x04, 0x6a, 0x4d, 0xf6, 0xb6, 0x7f, 0x86, 0x60, 0xbf, 0x4c, 0x6a, 0xe2, 0x76, 0x57, 0x27, 0x29,
0x6e, 0xa7, 0x49, 0x50, 0x98, 0xdb, 0x4a, 0x39, 0x73, 0xfb, 0x04, 0xc1, 0x9c, 0xe8, 0x3d, 0x17,
0x94, 0x0a, 0x4a, 0x73, 0x5a, 0x1f, 0x7a, 0xf9, 0x8b, 0xa6, 0xa6, 0xf1, 0x3d, 0x2e, 0xf6, 0x01,
0x6e, 0x16, 0x89, 0xf5, 0xbd, 0x76, 0xd8, 0x7c, 0x22, 0x3a, 0x8a, 0x4f, 0x9b, 0x8e, 0xd7, 0x09,
0xdf, 0x30, 0x70, 0x61, 0x42, 0x64, 0x6b, 0x2e, 0x20, 0x1c, 0x41, 0x8d, 0x19, 0x07, 0x6f, 0x27,
0xe0, 0xa5, 0xa1, 0xe6, 0xc3, 0x48, 0xa7, 0x41, 0xd7, 0x47, 0xda, 0x13, 0x69, 0x06, 0x14, 0xcf,
0x3e, 0x7c, 0xbc, 0x50, 0x2c, 0x17, 0xf4, 0x36, 0x82, 0x43, 0xaa, 0xb5, 0xc7, 0xe2, 0x4b, 0xdb,
0x7a, 0x11, 0x0a, 0x51, 0x54, 0xe3, 0x95, 0x52, 0x86, 0xc4, 0xe1, 0x5c, 0xbd, 0xf1, 0xa7, 0x67,
0xc7, 0xd0, 0x5f, 0x9f, 0x1d, 0x43, 0xff, 0x7a, 0x76, 0x0c, 0xbd, 0x71, 0xb9, 0xdc, 0x1f, 0x0f,
0x2d, 0xc7, 0xa6, 0x6e, 0xa4, 0xb2, 0xff, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x83, 0x0c, 0x3f,
0x61, 0x5e, 0x29, 0x00, 0x00,
0xdc, 0x10, 0x12, 0x17, 0xb8, 0x20, 0x90, 0x38, 0x20, 0x3e, 0x2e, 0x9c, 0x90, 0xc5, 0x8d, 0x0b,
0x07, 0xfe, 0x00, 0x54, 0xd5, 0x55, 0xdd, 0xd5, 0x33, 0x3d, 0x3d, 0xbd, 0xec, 0x46, 0xf1, 0xad,
0x5e, 0x4d, 0xd5, 0x7b, 0xbf, 0x7a, 0xf5, 0xbe, 0xea, 0xf5, 0xc0, 0xc9, 0x90, 0x06, 0x7d, 0x1a,
0x34, 0x89, 0xef, 0x3b, 0xb6, 0x45, 0x22, 0xdb, 0x73, 0xd5, 0x71, 0xc3, 0x0f, 0xbc, 0xc8, 0xc3,
0x75, 0x65, 0x4a, 0x5f, 0xec, 0x78, 0x5e, 0xc7, 0xa1, 0x4d, 0xe2, 0xdb, 0x4d, 0xe2, 0xba, 0x5e,
0xc4, 0xa7, 0xc3, 0x78, 0xa9, 0x6e, 0x6c, 0x5f, 0x0e, 0x1b, 0xb6, 0xc7, 0x7f, 0xb5, 0xbc, 0x80,
0x36, 0xfb, 0x17, 0x9b, 0x1d, 0xea, 0xd2, 0x80, 0x44, 0xb4, 0x2d, 0xd6, 0x5c, 0x4a, 0xd7, 0x74,
0x89, 0xb5, 0x65, 0xbb, 0x34, 0x18, 0x34, 0xfd, 0xed, 0x0e, 0x9b, 0x08, 0x9b, 0x5d, 0x1a, 0x91,
0xbc, 0x5d, 0xeb, 0x1d, 0x3b, 0xda, 0xea, 0xbd, 0xd9, 0xb0, 0xbc, 0x6e, 0x93, 0x04, 0x1d, 0xcf,
0x0f, 0xbc, 0xef, 0xf3, 0xc1, 0xaa, 0xd5, 0x6e, 0xf6, 0x5b, 0x29, 0x03, 0xf5, 0x2c, 0xfd, 0x8b,
0xc4, 0xf1, 0xb7, 0xc8, 0x28, 0xb7, 0xeb, 0x13, 0xb8, 0x05, 0xd4, 0xf7, 0x84, 0x6e, 0xf8, 0xd0,
0x8e, 0xbc, 0x60, 0xa0, 0x0c, 0x63, 0x36, 0xc6, 0x7f, 0x11, 0x1c, 0xbc, 0x92, 0xca, 0xfb, 0x66,
0x8f, 0x06, 0x03, 0x8c, 0x61, 0xda, 0x25, 0x5d, 0xaa, 0xa1, 0x25, 0xb4, 0x5c, 0x33, 0xf9, 0x18,
0x6b, 0x30, 0x17, 0xd0, 0xcd, 0x80, 0x86, 0x5b, 0x5a, 0x85, 0x4f, 0x4b, 0x12, 0xeb, 0x50, 0x65,
0xc2, 0xa9, 0x15, 0x85, 0xda, 0xd4, 0xd2, 0xd4, 0x72, 0xcd, 0x4c, 0x68, 0xbc, 0x0c, 0x07, 0x02,
0x1a, 0x7a, 0xbd, 0xc0, 0xa2, 0xdf, 0xa2, 0x41, 0x68, 0x7b, 0xae, 0x36, 0xcd, 0x77, 0x0f, 0x4f,
0x33, 0x2e, 0x21, 0x75, 0xa8, 0x15, 0x79, 0x81, 0x36, 0xc3, 0x97, 0x24, 0x34, 0xc3, 0xc3, 0x80,
0x6b, 0xb3, 0x31, 0x1e, 0x36, 0xc6, 0x06, 0xcc, 0x13, 0xdf, 0xbf, 0x4b, 0xba, 0x34, 0xf4, 0x89,
0x45, 0xb5, 0x39, 0xfe, 0x5b, 0x66, 0x8e, 0x61, 0x16, 0x48, 0xb4, 0x2a, 0x07, 0x26, 0x49, 0xe3,
0x1a, 0xd4, 0xee, 0x7a, 0x6d, 0x3a, 0xfe, 0xb8, 0xc3, 0xec, 0x2b, 0xa3, 0xec, 0x8d, 0x6d, 0x38,
0x6c, 0xd2, 0xbe, 0xcd, 0xe0, 0xdf, 0xa1, 0x11, 0x69, 0x93, 0x88, 0x0c, 0x33, 0xac, 0x24, 0x0c,
0x75, 0xa8, 0x06, 0x62, 0xb1, 0x56, 0xe1, 0xf3, 0x09, 0x3d, 0x22, 0x6c, 0x2a, 0x47, 0xd8, 0x9f,
0x10, 0x1c, 0x53, 0x2e, 0xca, 0x14, 0xea, 0xbb, 0xde, 0xa7, 0x6e, 0x14, 0x8e, 0x17, 0x7b, 0x1e,
0x0e, 0x49, 0x4d, 0x0f, 0x1f, 0x66, 0xf4, 0x07, 0x06, 0x44, 0x9d, 0x94, 0x40, 0xd4, 0x39, 0xbc,
0x04, 0x75, 0x49, 0x3f, 0xb8, 0xfd, 0x9a, 0xb8, 0x4e, 0x75, 0x6a, 0xe4, 0x38, 0x33, 0x39, 0xc7,
0x71, 0x41, 0x53, 0x4e, 0x73, 0x87, 0xb8, 0xf6, 0x26, 0x0d, 0xa3, 0xb2, 0xea, 0x43, 0x3b, 0x56,
0xdf, 0x71, 0xa8, 0xdd, 0xb0, 0x1d, 0x7a, 0x6d, 0xab, 0xe7, 0x6e, 0xe3, 0x17, 0x61, 0xc6, 0x62,
0x03, 0x2e, 0x61, 0xde, 0x8c, 0x09, 0xe3, 0x31, 0x1c, 0x1f, 0x07, 0xe9, 0xa1, 0x1d, 0x6d, 0xb1,
0xed, 0xe1, 0x38, 0x6c, 0xd6, 0x16, 0xb5, 0xb6, 0xc3, 0x5e, 0x57, 0x5e, 0xad, 0xa4, 0x4b, 0x61,
0xfb, 0x05, 0x82, 0xe5, 0x89, 0x92, 0x1f, 0x06, 0xc4, 0xf7, 0x69, 0x80, 0x6f, 0xc0, 0xcc, 0x23,
0xf6, 0x03, 0xb7, 0xd6, 0x7a, 0xab, 0xd1, 0x50, 0xa3, 0xdd, 0x44, 0x2e, 0xb7, 0x3e, 0x67, 0xc6,
0xdb, 0x71, 0x43, 0xea, 0xa0, 0xc2, 0xf9, 0x2c, 0x64, 0xf8, 0x24, 0xaa, 0x62, 0xeb, 0xf9, 0xb2,
0xab, 0xb3, 0x30, 0xed, 0x93, 0x20, 0x32, 0x0e, 0xc3, 0x0b, 0x59, 0x33, 0xf4, 0x3d, 0x37, 0xa4,
0xc6, 0x6f, 0x50, 0xe6, 0x42, 0xaf, 0x05, 0x94, 0x44, 0xd4, 0xa4, 0x8f, 0x7a, 0x34, 0x8c, 0xf0,
0x36, 0xa8, 0x01, 0x98, 0xeb, 0xae, 0xde, 0xba, 0xdd, 0x48, 0x23, 0x58, 0x43, 0x46, 0x30, 0x3e,
0xf8, 0xae, 0xd5, 0x6e, 0xf4, 0x5b, 0x0d, 0x7f, 0xbb, 0xd3, 0x60, 0xf1, 0x30, 0x83, 0x4c, 0xc6,
0x43, 0xf5, 0xa8, 0xa6, 0xca, 0x1d, 0x2f, 0xc0, 0x6c, 0xcf, 0x0f, 0x69, 0x10, 0xf1, 0x93, 0x55,
0x4d, 0x41, 0xb1, 0x5b, 0xea, 0x13, 0xc7, 0x6e, 0x93, 0x28, 0xbe, 0x85, 0xaa, 0x99, 0xd0, 0xc6,
0xc7, 0x59, 0xf4, 0x0f, 0xfc, 0xf6, 0x67, 0x85, 0x5e, 0x45, 0x59, 0x19, 0x42, 0xf9, 0x41, 0x16,
0xe5, 0x6b, 0xd4, 0xa1, 0x29, 0xca, 0x3c, 0xc3, 0xd4, 0x60, 0xce, 0x22, 0xa1, 0x45, 0xda, 0x92,
0x97, 0x24, 0x59, 0x58, 0xf0, 0x03, 0xcf, 0x27, 0x1d, 0xce, 0xe9, 0x9e, 0xe7, 0xd8, 0xd6, 0x40,
0xd8, 0xe6, 0xe8, 0x0f, 0x23, 0x46, 0x3c, 0x9d, 0x63, 0xc4, 0x27, 0xa0, 0xbe, 0x31, 0x70, 0xad,
0xd7, 0x7d, 0x9e, 0x4c, 0x99, 0x8b, 0xd9, 0x11, 0xed, 0x86, 0x1a, 0xe2, 0x81, 0x37, 0x26, 0x8c,
0x0f, 0x67, 0x60, 0x41, 0x39, 0x01, 0xdb, 0x50, 0x84, 0xbf, 0xc8, 0xe9, 0x17, 0x60, 0xb6, 0x1d,
0x0c, 0xcc, 0x9e, 0x2b, 0x2e, 0x53, 0x50, 0x4c, 0xb0, 0x1f, 0xf4, 0xdc, 0x18, 0x64, 0xd5, 0x8c,
0x09, 0xbc, 0x09, 0xd5, 0x30, 0x62, 0xe9, 0xb3, 0x33, 0xe0, 0xe1, 0xa8, 0xde, 0xfa, 0xfa, 0xee,
0x2e, 0x90, 0x41, 0xdf, 0x10, 0x1c, 0xcd, 0x84, 0x37, 0x7e, 0x04, 0x35, 0x19, 0x09, 0x43, 0x6d,
0x6e, 0x69, 0x6a, 0xb9, 0xde, 0xda, 0xd8, 0xbd, 0xa0, 0xd7, 0x7d, 0x96, 0xfa, 0x95, 0xa8, 0x6f,
0xa6, 0x52, 0xf0, 0x22, 0xd4, 0xba, 0xc2, 0xd7, 0x43, 0x91, 0xe6, 0xd2, 0x09, 0xfc, 0x6d, 0x98,
0xb1, 0xdd, 0x4d, 0x2f, 0xd4, 0x6a, 0x1c, 0xcc, 0xd5, 0xdd, 0x81, 0xb9, 0xed, 0x6e, 0x7a, 0x66,
0xcc, 0x10, 0x3f, 0x82, 0x7d, 0x01, 0x8d, 0x82, 0x81, 0xd4, 0x82, 0x06, 0x5c, 0xaf, 0xdf, 0xd8,
0x9d, 0x04, 0x53, 0x65, 0x69, 0x66, 0x25, 0xe0, 0x35, 0xa8, 0x87, 0xa9, 0x8d, 0x69, 0x75, 0x2e,
0x50, 0xcb, 0x30, 0x52, 0x6c, 0xd0, 0x54, 0x17, 0x8f, 0xd8, 0xf0, 0x7c, 0x8e, 0x0d, 0xff, 0x1d,
0xc1, 0xe2, 0x48, 0x18, 0xd8, 0xf0, 0x69, 0xa1, 0x91, 0x12, 0x98, 0x0e, 0x7d, 0x6a, 0xf1, 0xc8,
0x5f, 0x6f, 0xdd, 0xd9, 0xb3, 0xb8, 0xc0, 0xe5, 0x72, 0xd6, 0x45, 0xa1, 0xab, 0x94, 0x6f, 0xfe,
0x08, 0xc1, 0xe7, 0x15, 0xce, 0xf7, 0x48, 0x64, 0x6d, 0x15, 0x1d, 0x89, 0xf9, 0x10, 0x5b, 0x23,
0xb2, 0x59, 0x4c, 0x30, 0x43, 0xe3, 0x83, 0xfb, 0x03, 0x9f, 0xc1, 0x60, 0xbf, 0xa4, 0x13, 0xa5,
0x92, 0xfe, 0xbb, 0x08, 0x74, 0x35, 0xf2, 0x79, 0x8e, 0xf3, 0x26, 0xb1, 0xb6, 0x8b, 0xa0, 0xec,
0x87, 0x8a, 0xdd, 0xe6, 0x38, 0xa6, 0xcc, 0x8a, 0xdd, 0xde, 0xa1, 0xdb, 0x0f, 0x83, 0x9a, 0xcd,
0x01, 0xf5, 0x8f, 0x21, 0x50, 0xd2, 0xc5, 0x0a, 0x40, 0x2d, 0x42, 0xcd, 0x1d, 0x2a, 0xa6, 0xd2,
0x89, 0x9c, 0x22, 0xaa, 0x32, 0x52, 0x44, 0x69, 0x30, 0xd7, 0x4f, 0xea, 0x61, 0xf6, 0xb3, 0x24,
0xd9, 0x41, 0x3a, 0x81, 0xd7, 0xf3, 0x85, 0x02, 0x63, 0x82, 0xa1, 0xd8, 0xb6, 0xdd, 0xb6, 0x36,
0x1b, 0xa3, 0x60, 0xe3, 0x32, 0x15, 0xb0, 0xf1, 0x5e, 0x05, 0xbe, 0x90, 0x73, 0xb8, 0x89, 0x16,
0xf0, 0x7c, 0x9c, 0x30, 0xb1, 0xc3, 0xb9, 0xb1, 0x76, 0x58, 0x9d, 0x64, 0x87, 0xb5, 0x1c, 0xad,
0xbc, 0x53, 0x81, 0xa5, 0x1c, 0xad, 0x4c, 0x4e, 0xa8, 0xcf, 0x8d, 0x5a, 0x36, 0xbd, 0x40, 0xdc,
0x78, 0xd5, 0x8c, 0x09, 0xe6, 0x19, 0x5e, 0xe0, 0x6f, 0x11, 0x57, 0xab, 0xc6, 0x9e, 0x11, 0x53,
0xa5, 0x14, 0xf2, 0x1f, 0x04, 0x9a, 0xd4, 0xc2, 0x15, 0x8b, 0xeb, 0xa4, 0xe7, 0x3e, 0xff, 0x8a,
0x58, 0x80, 0x59, 0xc2, 0xd1, 0x0a, 0x03, 0x11, 0xd4, 0xc8, 0x91, 0xab, 0xf9, 0x31, 0xf1, 0x48,
0xf6, 0xc8, 0xe1, 0xba, 0x1d, 0x46, 0xb2, 0xa0, 0xc5, 0x9b, 0x30, 0x17, 0x73, 0x8b, 0x4b, 0x98,
0x7a, 0x6b, 0x7d, 0xb7, 0x89, 0x2d, 0xa3, 0x5e, 0xc9, 0xdc, 0x78, 0x19, 0x8e, 0xe4, 0x46, 0x1f,
0x01, 0x43, 0x87, 0xaa, 0x4c, 0xe6, 0xe2, 0x02, 0x12, 0xda, 0xf8, 0xf7, 0x54, 0x36, 0xac, 0x7b,
0xed, 0x75, 0xaf, 0x53, 0xf0, 0x16, 0x2c, 0xbe, 0x34, 0xf6, 0x58, 0xf6, 0xda, 0xca, 0xb3, 0x4f,
0x92, 0x6c, 0x9f, 0xe5, 0xb9, 0x11, 0xb1, 0x5d, 0x1a, 0x88, 0xfc, 0x92, 0x4e, 0x30, 0x65, 0x87,
0xb6, 0x6b, 0xd1, 0x0d, 0x6a, 0x79, 0x6e, 0x3b, 0xe4, 0xb7, 0x36, 0x65, 0x66, 0xe6, 0xf0, 0x2d,
0xa8, 0x71, 0xfa, 0xbe, 0xdd, 0x8d, 0x83, 0x70, 0xbd, 0xb5, 0xd2, 0x88, 0x9b, 0x28, 0x0d, 0xb5,
0x89, 0x92, 0xea, 0xb0, 0x4b, 0x23, 0xd2, 0xe8, 0x5f, 0x6c, 0xb0, 0x1d, 0x66, 0xba, 0x99, 0x61,
0x89, 0x88, 0xed, 0xac, 0xdb, 0x2e, 0x2f, 0xb0, 0x98, 0xa8, 0x74, 0x82, 0x19, 0xc4, 0xa6, 0xe7,
0x38, 0xde, 0x63, 0xe9, 0x03, 0x31, 0xc5, 0x76, 0xf5, 0xdc, 0xc8, 0x76, 0xb8, 0xfc, 0xd8, 0x01,
0xd2, 0x09, 0xbe, 0xcb, 0x76, 0x22, 0x1a, 0xf0, 0x12, 0xa6, 0x66, 0x0a, 0x2a, 0x31, 0xb9, 0x7a,
0xdc, 0x17, 0x90, 0xbe, 0x17, 0x1b, 0xe7, 0xbc, 0x6a, 0x9c, 0xc3, 0x06, 0xbf, 0x2f, 0xe7, 0xdd,
0xcc, 0xdb, 0x24, 0xb4, 0x6f, 0x7b, 0xbd, 0x50, 0xdb, 0x1f, 0x27, 0x71, 0x49, 0x8f, 0x18, 0xec,
0x81, 0x1c, 0x83, 0xfd, 0x2d, 0x82, 0xea, 0xba, 0xd7, 0xb9, 0xee, 0x46, 0xc1, 0x80, 0x57, 0xf6,
0x9e, 0x1b, 0x51, 0x57, 0x5a, 0x85, 0x24, 0x99, 0xaa, 0x23, 0xbb, 0x4b, 0x37, 0x22, 0xd2, 0xf5,
0x45, 0x4d, 0xb2, 0x23, 0x55, 0x27, 0x9b, 0xd9, 0xf1, 0x1d, 0x12, 0x46, 0xdc, 0x7b, 0xab, 0x26,
0x1f, 0x33, 0xa0, 0xc9, 0x82, 0x8d, 0x28, 0x10, 0xae, 0x9b, 0x99, 0x53, 0x0d, 0x69, 0x26, 0xc6,
0x26, 0x48, 0x63, 0x03, 0x5e, 0x4a, 0x4a, 0xd9, 0xfb, 0x34, 0xe8, 0xda, 0x2e, 0x29, 0x8e, 0xb7,
0x65, 0xba, 0x30, 0x0f, 0x32, 0x0e, 0xc4, 0xea, 0xbf, 0x87, 0xb6, 0xdb, 0xf6, 0x1e, 0x17, 0x38,
0x42, 0x19, 0xb6, 0x7f, 0xc9, 0xf6, 0x5b, 0x14, 0xbe, 0x89, 0x6f, 0xde, 0x82, 0x7d, 0xcc, 0x8b,
0xfb, 0x54, 0xfc, 0x20, 0x02, 0x85, 0x31, 0xee, 0x49, 0x9e, 0xf2, 0x30, 0xb3, 0x1b, 0xf1, 0x3a,
0x1c, 0x20, 0x61, 0x68, 0x77, 0x5c, 0xda, 0x96, 0xbc, 0x2a, 0xa5, 0x79, 0x0d, 0x6f, 0x8d, 0x9f,
0x7d, 0x7c, 0x85, 0xb8, 0x3b, 0x49, 0x1a, 0x3f, 0x44, 0x70, 0x38, 0x97, 0x49, 0x62, 0xeb, 0x48,
0x09, 0xaf, 0x3a, 0x54, 0x43, 0x6b, 0x8b, 0xb6, 0x7b, 0x0e, 0x95, 0x7d, 0x0d, 0x49, 0xb3, 0xdf,
0xda, 0xbd, 0xf8, 0x26, 0x45, 0x78, 0x4f, 0x68, 0x7c, 0x0c, 0xa0, 0x4b, 0xdc, 0x1e, 0x71, 0x38,
0x84, 0x69, 0x0e, 0x41, 0x99, 0x31, 0x16, 0x41, 0xcf, 0x33, 0x03, 0xd1, 0x49, 0xf8, 0x1b, 0x82,
0xfd, 0x32, 0x0c, 0x8a, 0x3b, 0x5c, 0x86, 0x03, 0x8a, 0x1a, 0xee, 0xa6, 0xd7, 0x39, 0x3c, 0x3d,
0x21, 0xc4, 0x49, 0x5b, 0x98, 0xca, 0xf6, 0x35, 0xfb, 0x99, 0xce, 0x64, 0xe9, 0x3c, 0x84, 0x76,
0x54, 0x89, 0xfd, 0x00, 0xb4, 0x3b, 0xc4, 0x25, 0x1d, 0xda, 0x4e, 0x0e, 0x97, 0x18, 0xd2, 0xf7,
0xd4, 0xc7, 0xf2, 0xae, 0x9f, 0xa6, 0x49, 0x39, 0x63, 0x6f, 0x6e, 0xca, 0x87, 0x77, 0x00, 0xd5,
0x75, 0xdb, 0xdd, 0x66, 0xef, 0x37, 0x76, 0xae, 0xc8, 0x8e, 0x1c, 0xa9, 0xc3, 0x98, 0xc0, 0x07,
0x61, 0xaa, 0x17, 0x38, 0xe2, 0x9e, 0xd9, 0x10, 0x2f, 0x41, 0xbd, 0x4d, 0x43, 0x2b, 0xb0, 0x7d,
0x71, 0xcb, 0xbc, 0xd1, 0xa7, 0x4c, 0x31, 0x6d, 0xdb, 0x96, 0xe7, 0x5e, 0x73, 0x48, 0x18, 0xca,
0xc4, 0x90, 0x4c, 0x18, 0xaf, 0xc2, 0x3e, 0x26, 0x33, 0x3d, 0xe6, 0xb9, 0xec, 0x31, 0x0f, 0x67,
0xe0, 0x4b, 0x78, 0x12, 0xf1, 0x4d, 0x78, 0x81, 0xe5, 0xe3, 0x2b, 0xbe, 0x2f, 0x98, 0x94, 0x2c,
0x46, 0xa6, 0x86, 0x2e, 0xbd, 0xf5, 0x63, 0x03, 0xb0, 0x6a, 0xf3, 0x34, 0xe8, 0xdb, 0x16, 0xc5,
0xef, 0x22, 0x98, 0x66, 0x02, 0xf0, 0xd1, 0x71, 0x2e, 0xc6, 0x6d, 0x4f, 0xdf, 0xbb, 0x07, 0x1d,
0x93, 0x66, 0x2c, 0xbe, 0xf5, 0xd7, 0x7f, 0xbd, 0x57, 0x59, 0xc0, 0x2f, 0xf2, 0x0f, 0x0c, 0xfd,
0x8b, 0x6a, 0xb3, 0x3f, 0xc4, 0x6f, 0x23, 0xc0, 0xa2, 0x0a, 0x51, 0xba, 0xbb, 0xf8, 0xdc, 0x38,
0x88, 0x39, 0x5d, 0x60, 0xfd, 0xa8, 0x12, 0xed, 0x1b, 0x96, 0x17, 0x50, 0x16, 0xdb, 0xf9, 0x02,
0x0e, 0x60, 0x85, 0x03, 0x38, 0x89, 0x8d, 0x3c, 0x00, 0xcd, 0x27, 0x4c, 0x6f, 0x4f, 0x9b, 0x34,
0x96, 0xfb, 0x11, 0x82, 0x99, 0x87, 0xbc, 0xe6, 0x9e, 0xa0, 0xa4, 0x8d, 0x3d, 0x53, 0x12, 0x17,
0xc7, 0xd1, 0x1a, 0x27, 0x38, 0xd2, 0xa3, 0xf8, 0x88, 0x44, 0x1a, 0x46, 0x01, 0x25, 0xdd, 0x0c,
0xe0, 0x0b, 0x08, 0x7f, 0x82, 0x60, 0x36, 0x6e, 0x37, 0xe2, 0x53, 0xe3, 0x50, 0x66, 0xda, 0x91,
0xfa, 0xde, 0xf5, 0xee, 0x8c, 0xb3, 0x1c, 0xe3, 0x09, 0x23, 0xf7, 0x3a, 0xd7, 0x32, 0x9d, 0xbd,
0xf7, 0x11, 0x4c, 0xdd, 0xa4, 0x13, 0xed, 0x6d, 0x0f, 0xc1, 0x8d, 0x28, 0x30, 0xe7, 0xaa, 0xf1,
0xc7, 0x08, 0x5e, 0xba, 0x49, 0xa3, 0xfc, 0x54, 0x87, 0x97, 0x27, 0xe7, 0x1f, 0x61, 0x76, 0xe7,
0x4a, 0xac, 0x4c, 0x62, 0x7c, 0x93, 0x23, 0x3b, 0x8b, 0xcf, 0x14, 0x19, 0x61, 0x38, 0x70, 0xad,
0xc7, 0x02, 0xc7, 0x1f, 0x11, 0x1c, 0x1c, 0xfe, 0xd6, 0x82, 0xb3, 0xc9, 0x31, 0xf7, 0x53, 0x8c,
0x7e, 0x77, 0xb7, 0xb1, 0x34, 0xcb, 0xd4, 0xb8, 0xc2, 0x91, 0xbf, 0x82, 0x5f, 0x2e, 0x42, 0x2e,
0x9b, 0x94, 0x61, 0xf3, 0x89, 0x1c, 0x3e, 0xe5, 0x9f, 0x05, 0x39, 0xec, 0xb7, 0x10, 0xcc, 0xdf,
0xa4, 0xd1, 0x9d, 0xa4, 0x47, 0x77, 0xaa, 0x54, 0x0f, 0x5f, 0x5f, 0x6c, 0x28, 0x5f, 0xef, 0xe4,
0x4f, 0x89, 0x4a, 0x57, 0x39, 0xb0, 0x33, 0xf8, 0x54, 0x11, 0xb0, 0xb4, 0x2f, 0xf8, 0x11, 0x82,
0xc3, 0x2a, 0x88, 0xf4, 0x0b, 0xc7, 0x97, 0x76, 0xf6, 0x45, 0x41, 0x7c, 0x97, 0x98, 0x80, 0xae,
0xc5, 0xd1, 0x9d, 0x37, 0xf2, 0x2f, 0xbc, 0x3b, 0x82, 0x62, 0x0d, 0xad, 0x2c, 0x23, 0xfc, 0x3b,
0x04, 0xb3, 0x71, 0x13, 0x6e, 0xbc, 0x8e, 0x32, 0xbd, 0xfa, 0xbd, 0xf4, 0x9e, 0xeb, 0x1c, 0xf2,
0x57, 0xf5, 0x0b, 0xf9, 0x0a, 0x55, 0xf7, 0xcb, 0xab, 0x6d, 0x70, 0x2d, 0x67, 0xdd, 0xfe, 0x57,
0x08, 0x20, 0x6d, 0x24, 0xe2, 0xb3, 0xc5, 0xe7, 0x50, 0x9a, 0x8d, 0xfa, 0xde, 0xb6, 0x12, 0x8d,
0x06, 0x3f, 0xcf, 0xb2, 0xbe, 0x54, 0xe8, 0x73, 0x3e, 0xb5, 0xd6, 0xe2, 0xa6, 0xe3, 0xcf, 0x11,
0xcc, 0xf0, 0x3e, 0x11, 0x3e, 0x39, 0x0e, 0xb3, 0xda, 0x46, 0xda, 0x4b, 0xd5, 0x9f, 0xe6, 0x50,
0x97, 0x5a, 0x45, 0x81, 0x6b, 0x0d, 0xad, 0xe0, 0x3e, 0xcc, 0xc6, 0x3d, 0x9b, 0xf1, 0xe6, 0x91,
0xe9, 0xe9, 0xe8, 0x4b, 0x05, 0x89, 0x34, 0x36, 0x54, 0x11, 0x33, 0x57, 0x26, 0xc5, 0xcc, 0x69,
0x16, 0xd6, 0xf0, 0x89, 0xa2, 0xa0, 0xf7, 0x29, 0x28, 0xe6, 0x1c, 0x47, 0x77, 0xca, 0x58, 0x9a,
0x14, 0x37, 0x99, 0x76, 0x7e, 0x8a, 0xe0, 0xe0, 0x70, 0xc9, 0x89, 0x8f, 0x0c, 0xc5, 0x4c, 0xb5,
0xce, 0xd6, 0xb3, 0x5a, 0x1c, 0x57, 0xae, 0x1a, 0x5f, 0xe3, 0x28, 0xd6, 0xf0, 0xe5, 0x89, 0x9e,
0x71, 0x57, 0x46, 0x1d, 0xc6, 0x68, 0x35, 0xfd, 0x66, 0xf1, 0x6b, 0x04, 0xf3, 0x92, 0xef, 0xfd,
0x80, 0xd2, 0x62, 0x58, 0x7b, 0xe7, 0x08, 0x4c, 0x96, 0xf1, 0x2a, 0x87, 0xff, 0x65, 0x7c, 0xa9,
0x24, 0x7c, 0x09, 0x7b, 0x35, 0x62, 0x48, 0x7f, 0x8f, 0xe0, 0xd0, 0xc3, 0xd8, 0xee, 0x3f, 0x23,
0xfc, 0xd7, 0x38, 0xfe, 0xaf, 0xe0, 0x57, 0x0a, 0xea, 0xa2, 0x49, 0xc7, 0xb8, 0x80, 0xf0, 0x2f,
0x11, 0x54, 0x65, 0x07, 0x1e, 0x9f, 0x19, 0xeb, 0x18, 0xd9, 0x1e, 0xfd, 0x5e, 0x1a, 0xb3, 0x28,
0x02, 0x8c, 0x93, 0x85, 0xa9, 0x54, 0xc8, 0x67, 0x06, 0xfd, 0x3e, 0x02, 0x9c, 0xbc, 0x17, 0x93,
0x17, 0x24, 0x3e, 0x9d, 0x11, 0x35, 0xb6, 0xc1, 0xa0, 0x9f, 0x99, 0xb8, 0x2e, 0x9b, 0x4a, 0x57,
0x0a, 0x53, 0xa9, 0x97, 0xc8, 0x7f, 0x07, 0x41, 0xfd, 0x26, 0x4d, 0x6a, 0xf6, 0x02, 0x5d, 0x66,
0x3f, 0x2d, 0xe8, 0xcb, 0x93, 0x17, 0x0a, 0x44, 0xe7, 0x39, 0xa2, 0xd3, 0xb8, 0x58, 0x55, 0x12,
0xc0, 0x87, 0x08, 0xf6, 0xdd, 0x53, 0x4d, 0x14, 0x9f, 0x9f, 0x24, 0x29, 0x13, 0xc9, 0xcb, 0xe3,
0xfa, 0x22, 0xc7, 0xb5, 0x6a, 0x94, 0xc2, 0xb5, 0x26, 0xfa, 0xf7, 0x3f, 0x43, 0xf1, 0xd3, 0x6e,
0xa8, 0xfb, 0xfa, 0xff, 0xea, 0xad, 0xa0, 0x89, 0x6b, 0x5c, 0xe2, 0xf8, 0x1a, 0xf8, 0x7c, 0x19,
0x7c, 0x4d, 0xd1, 0x92, 0xc5, 0x1f, 0x20, 0x38, 0xc4, 0xfb, 0xdf, 0x2a, 0xe3, 0xa1, 0x14, 0x33,
0xae, 0x5b, 0x5e, 0x22, 0xc5, 0x88, 0xf8, 0x63, 0xec, 0x08, 0xd4, 0x9a, 0xec, 0x6d, 0xff, 0x04,
0xc1, 0x7e, 0x99, 0xd4, 0xc4, 0xed, 0xae, 0x4e, 0x52, 0xdc, 0x4e, 0x93, 0xa0, 0x30, 0xb7, 0x95,
0x72, 0xe6, 0xf6, 0x09, 0x82, 0x39, 0xd1, 0x7b, 0x2e, 0x28, 0x15, 0x94, 0xe6, 0xb4, 0x3e, 0xf4,
0xf2, 0x17, 0x4d, 0x4d, 0xe3, 0x3b, 0x5c, 0xec, 0x03, 0xdc, 0x2c, 0x12, 0xeb, 0x7b, 0xed, 0xb0,
0xf9, 0x44, 0x74, 0x14, 0x9f, 0x36, 0x1d, 0xaf, 0x13, 0xbe, 0x61, 0xe0, 0xc2, 0x84, 0xc8, 0xd6,
0x5c, 0x40, 0x38, 0x82, 0x1a, 0x33, 0x0e, 0xde, 0x4e, 0xc0, 0x4b, 0x43, 0xcd, 0x87, 0x91, 0x4e,
0x83, 0xae, 0x8f, 0xb4, 0x27, 0xd2, 0x0c, 0x28, 0x9e, 0x7d, 0xf8, 0x78, 0xa1, 0x58, 0x2e, 0xe8,
0x6d, 0x04, 0x87, 0x54, 0x6b, 0x8f, 0xc5, 0x97, 0xb6, 0xf5, 0x22, 0x14, 0xa2, 0xa8, 0xc6, 0x2b,
0xa5, 0x0c, 0x89, 0xc3, 0xb9, 0x7a, 0xe3, 0x0f, 0xcf, 0x8e, 0xa1, 0x3f, 0x3f, 0x3b, 0x86, 0xfe,
0xf9, 0xec, 0x18, 0x7a, 0xe3, 0x72, 0xb9, 0xbf, 0x24, 0x5a, 0x8e, 0x4d, 0xdd, 0x48, 0x65, 0xff,
0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x96, 0x44, 0xa9, 0x6d, 0x78, 0x29, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -4002,6 +4011,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.AppNamespace != nil {
i -= len(*m.AppNamespace)
copy(dAtA[i:], *m.AppNamespace)
@@ -6241,6 +6259,12 @@ func (m *ApplicationQuery) Size() (n int) {
l = len(*m.AppNamespace)
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)
}
@@ -7474,6 +7498,38 @@ func (m *ApplicationQuery) Unmarshal(dAtA []byte) error {
s := string(dAtA[iNdEx:postIndex])
m.AppNamespace = &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

@@ -19,7 +19,7 @@ const (
AppProjectFullName string = AppProjectPlural + "." + Group
// ApplicationSet constants
ApplicationSetKind string = "Applicationset"
ApplicationSetKind string = "ApplicationSet"
ApplicationSetSingular string = "applicationset"
ApplicationSetShortName string = "appset"
ApplicationSetPlural string = "applicationsets"

View File

@@ -202,7 +202,7 @@ func (repo *Repository) GetGitCreds(store git.CredsStore) git.Creds {
return git.NewGitHubAppCreds(repo.GithubAppId, repo.GithubAppInstallationId, repo.GithubAppPrivateKey, repo.GitHubAppEnterpriseBaseURL, repo.Repo, repo.TLSClientCertData, repo.TLSClientCertKey, repo.IsInsecure(), repo.Proxy, store)
}
if repo.GCPServiceAccountKey != "" {
return git.NewGoogleCloudCreds(repo.GCPServiceAccountKey)
return git.NewGoogleCloudCreds(repo.GCPServiceAccountKey, store)
}
return git.NopCreds{}
}

View File

@@ -0,0 +1,167 @@
// Code generated by mockery v2.13.1. DO NOT EDIT.
package mocks
import (
context "context"
apiclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient"
metadata "google.golang.org/grpc/metadata"
mock "github.com/stretchr/testify/mock"
)
// RepoServerService_GenerateManifestWithFilesClient is an autogenerated mock type for the RepoServerService_GenerateManifestWithFilesClient type
type RepoServerService_GenerateManifestWithFilesClient struct {
mock.Mock
}
// CloseAndRecv provides a mock function with given fields:
func (_m *RepoServerService_GenerateManifestWithFilesClient) CloseAndRecv() (*apiclient.ManifestResponse, error) {
ret := _m.Called()
var r0 *apiclient.ManifestResponse
if rf, ok := ret.Get(0).(func() *apiclient.ManifestResponse); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*apiclient.ManifestResponse)
}
}
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CloseSend provides a mock function with given fields:
func (_m *RepoServerService_GenerateManifestWithFilesClient) CloseSend() error {
ret := _m.Called()
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
} else {
r0 = ret.Error(0)
}
return r0
}
// Context provides a mock function with given fields:
func (_m *RepoServerService_GenerateManifestWithFilesClient) Context() context.Context {
ret := _m.Called()
var r0 context.Context
if rf, ok := ret.Get(0).(func() context.Context); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(context.Context)
}
}
return r0
}
// Header provides a mock function with given fields:
func (_m *RepoServerService_GenerateManifestWithFilesClient) Header() (metadata.MD, error) {
ret := _m.Called()
var r0 metadata.MD
if rf, ok := ret.Get(0).(func() metadata.MD); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(metadata.MD)
}
}
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RecvMsg provides a mock function with given fields: m
func (_m *RepoServerService_GenerateManifestWithFilesClient) RecvMsg(m interface{}) error {
ret := _m.Called(m)
var r0 error
if rf, ok := ret.Get(0).(func(interface{}) error); ok {
r0 = rf(m)
} else {
r0 = ret.Error(0)
}
return r0
}
// Send provides a mock function with given fields: _a0
func (_m *RepoServerService_GenerateManifestWithFilesClient) Send(_a0 *apiclient.ManifestRequestWithFiles) error {
ret := _m.Called(_a0)
var r0 error
if rf, ok := ret.Get(0).(func(*apiclient.ManifestRequestWithFiles) error); ok {
r0 = rf(_a0)
} else {
r0 = ret.Error(0)
}
return r0
}
// SendMsg provides a mock function with given fields: m
func (_m *RepoServerService_GenerateManifestWithFilesClient) SendMsg(m interface{}) error {
ret := _m.Called(m)
var r0 error
if rf, ok := ret.Get(0).(func(interface{}) error); ok {
r0 = rf(m)
} else {
r0 = ret.Error(0)
}
return r0
}
// Trailer provides a mock function with given fields:
func (_m *RepoServerService_GenerateManifestWithFilesClient) Trailer() metadata.MD {
ret := _m.Called()
var r0 metadata.MD
if rf, ok := ret.Get(0).(func() metadata.MD); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(metadata.MD)
}
}
return r0
}
type mockConstructorTestingTNewRepoServerService_GenerateManifestWithFilesClient interface {
mock.TestingT
Cleanup(func())
}
// NewRepoServerService_GenerateManifestWithFilesClient creates a new instance of RepoServerService_GenerateManifestWithFilesClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewRepoServerService_GenerateManifestWithFilesClient(t mockConstructorTestingTNewRepoServerService_GenerateManifestWithFilesClient) *RepoServerService_GenerateManifestWithFilesClient {
mock := &RepoServerService_GenerateManifestWithFilesClient{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@@ -221,7 +221,7 @@ func (s *Service) ListApps(ctx context.Context, q *apiclient.ListAppsRequest) (*
}
defer io.Close(closer)
apps, err := discovery.Discover(ctx, gitClient.Root(), q.EnabledSourceTypes, s.initConstants.CMPTarExcludedGlobs)
apps, err := discovery.Discover(ctx, gitClient.Root(), gitClient.Root(), q.EnabledSourceTypes, s.initConstants.CMPTarExcludedGlobs)
if err != nil {
return nil, err
}
@@ -474,6 +474,7 @@ func resolveReferencedSources(hasMultipleSources bool, source *v1alpha1.Applicat
if !ok {
_, referencedCommitSHA, err := newClientResolveRevision(&refSourceMapping.Repo, refSourceMapping.TargetRevision)
if err != nil {
log.Errorf("Failed to get git client for repo %s: %v", refSourceMapping.Repo.Repo, err)
return nil, fmt.Errorf("failed to get git client for repo %s", refSourceMapping.Repo.Repo)
}
@@ -706,6 +707,7 @@ func (s *Service) runManifestGenAsync(ctx context.Context, repoRoot, commitSHA,
} else {
gitClient, referencedCommitSHA, err := s.newClientResolveRevision(&refSourceMapping.Repo, refSourceMapping.TargetRevision)
if err != nil {
log.Errorf("Failed to get git client for repo %s: %v", refSourceMapping.Repo.Repo, err)
ch.errCh <- fmt.Errorf("failed to get git client for repo %s", refSourceMapping.Repo.Repo)
return
}
@@ -1292,7 +1294,7 @@ func GenerateManifests(ctx context.Context, appPath, repoRoot, revision string,
resourceTracking := argo.NewResourceTracking()
appSourceType, err := GetAppSourceType(ctx, q.ApplicationSource, appPath, q.AppName, q.EnabledSourceTypes, opt.cmpTarExcludedGlobs)
appSourceType, err := GetAppSourceType(ctx, q.ApplicationSource, appPath, repoRoot, q.AppName, q.EnabledSourceTypes, opt.cmpTarExcludedGlobs)
if err != nil {
return nil, err
}
@@ -1469,8 +1471,8 @@ func mergeSourceParameters(source *v1alpha1.ApplicationSource, path, appName str
}
// GetAppSourceType returns explicit application source type or examines a directory and determines its application source type
func GetAppSourceType(ctx context.Context, source *v1alpha1.ApplicationSource, path, appName string, enableGenerateManifests map[string]bool, tarExcludedGlobs []string) (v1alpha1.ApplicationSourceType, error) {
err := mergeSourceParameters(source, path, appName)
func GetAppSourceType(ctx context.Context, source *v1alpha1.ApplicationSource, appPath, repoPath, appName string, enableGenerateManifests map[string]bool, tarExcludedGlobs []string) (v1alpha1.ApplicationSourceType, error) {
err := mergeSourceParameters(source, appPath, appName)
if err != nil {
return "", fmt.Errorf("error while parsing source parameters: %v", err)
}
@@ -1486,7 +1488,7 @@ func GetAppSourceType(ctx context.Context, source *v1alpha1.ApplicationSource, p
}
return *appSourceType, nil
}
appType, err := discovery.AppType(ctx, path, enableGenerateManifests, tarExcludedGlobs)
appType, err := discovery.AppType(ctx, appPath, repoPath, enableGenerateManifests, tarExcludedGlobs)
if err != nil {
return "", err
}
@@ -1906,7 +1908,7 @@ func runConfigManagementPluginSidecars(ctx context.Context, appPath, repoPath, p
}
// detect config management plugin server (sidecar)
conn, cmpClient, err := discovery.DetectConfigManagementPlugin(ctx, appPath, pluginName, env, tarExcludedGlobs)
conn, cmpClient, err := discovery.DetectConfigManagementPlugin(ctx, appPath, repoPath, pluginName, env, tarExcludedGlobs)
if err != nil {
return nil, err
}
@@ -1963,7 +1965,7 @@ func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppD
return err
}
appSourceType, err := GetAppSourceType(ctx, q.Source, opContext.appPath, q.AppName, q.EnabledSourceTypes, s.initConstants.CMPTarExcludedGlobs)
appSourceType, err := GetAppSourceType(ctx, q.Source, opContext.appPath, repoRoot, q.AppName, q.EnabledSourceTypes, s.initConstants.CMPTarExcludedGlobs)
if err != nil {
return err
}
@@ -2157,7 +2159,7 @@ func populatePluginAppDetails(ctx context.Context, res *apiclient.RepoAppDetails
pluginName = q.Source.Plugin.Name
}
// detect config management plugin server (sidecar)
conn, cmpClient, err := discovery.DetectConfigManagementPlugin(ctx, appPath, pluginName, env, tarExcludedGlobs)
conn, cmpClient, err := discovery.DetectConfigManagementPlugin(ctx, appPath, repoPath, pluginName, env, tarExcludedGlobs)
if err != nil {
return fmt.Errorf("failed to detect CMP for app: %w", err)
}

View File

@@ -1188,15 +1188,15 @@ func TestGenerateNullList(t *testing.T) {
}
func TestIdentifyAppSourceTypeByAppDirWithKustomizations(t *testing.T) {
sourceType, err := GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/kustomization_yaml", "testapp", map[string]bool{}, []string{})
sourceType, err := GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/kustomization_yaml", "./testdata", "testapp", map[string]bool{}, []string{})
assert.Nil(t, err)
assert.Equal(t, argoappv1.ApplicationSourceTypeKustomize, sourceType)
sourceType, err = GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/kustomization_yml", "testapp", map[string]bool{}, []string{})
sourceType, err = GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/kustomization_yml", "./testdata", "testapp", map[string]bool{}, []string{})
assert.Nil(t, err)
assert.Equal(t, argoappv1.ApplicationSourceTypeKustomize, sourceType)
sourceType, err = GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/Kustomization", "testapp", map[string]bool{}, []string{})
sourceType, err = GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/Kustomization", "./testdata", "testapp", map[string]bool{}, []string{})
assert.Nil(t, err)
assert.Equal(t, argoappv1.ApplicationSourceTypeKustomize, sourceType)
}

View File

@@ -68,7 +68,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
@@ -78,7 +79,7 @@ type Server struct {
appclientset appclientset.Interface
appLister applisters.ApplicationLister
appInformer cache.SharedIndexInformer
appBroadcaster *broadcasterHandler
appBroadcaster Broadcaster
repoClientset apiclient.Clientset
kubectl kube.Kubectl
db db.ArgoDB
@@ -98,6 +99,7 @@ func NewServer(
appclientset appclientset.Interface,
appLister applisters.ApplicationLister,
appInformer cache.SharedIndexInformer,
appBroadcaster Broadcaster,
repoClientset apiclient.Clientset,
cache *servercache.Cache,
kubectl kube.Kubectl,
@@ -108,7 +110,9 @@ func NewServer(
projInformer cache.SharedIndexInformer,
enabledNamespaces []string,
) (application.ApplicationServiceServer, AppResourceTreeFn) {
appBroadcaster := &broadcasterHandler{}
if appBroadcaster == nil {
appBroadcaster = &broadcasterHandler{}
}
appInformer.AddEventHandler(appBroadcaster)
s := &Server{
ns: namespace,
@@ -131,6 +135,61 @@ 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, namespace, name string, getApp func() (*appv1.Application, error)) (*appv1.Application, error) {
logCtx := log.WithFields(map[string]interface{}{
"application": name,
"namespace": namespace,
})
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, a.RBACName(s.ns)); err != nil {
logCtx.WithFields(map[string]interface{}{
"project": a.Spec.Project,
argocommon.SecurityField: argocommon.SecurityMedium,
}).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, namespace, name string) (*appv1.Application, error) {
namespaceOrDefault := s.appNamespaceOrDefault(namespace)
return s.getAppEnforceRBAC(ctx, action, namespaceOrDefault, name, func() (*appv1.Application, error) {
return s.appLister.Applications(namespaceOrDefault).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, namespace, name, resourceVersion string) (*appv1.Application, error) {
namespaceOrDefault := s.appNamespaceOrDefault(namespace)
return s.getAppEnforceRBAC(ctx, action, namespaceOrDefault, name, func() (*appv1.Application, error) {
return s.appclientset.ArgoprojV1alpha1().Applications(namespaceOrDefault).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) {
selector, err := labels.Parse(q.GetSelector())
@@ -148,7 +207,7 @@ func (s *Server) List(ctx context.Context, q *application.ApplicationQuery) (*ap
}
newItems := make([]appv1.Application, 0)
for _, a := range apps {
// Skip any application that is neither in the conrol plane's namespace
// Skip any application that is neither in the control plane's namespace
// nor in the list of enabled namespaces.
if a.Namespace != s.ns && !glob.MatchStringInList(s.enabledNamespaces, a.Namespace, false) {
continue
@@ -165,8 +224,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())
@@ -318,13 +377,8 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan
if q.Name == nil || *q.Name == "" {
return nil, fmt.Errorf("invalid request: application name is missing")
}
appName := q.GetName()
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
a, err := s.appLister.Applications(appNs).Get(appName)
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetAppNamespace(), 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, a.RBACName(s.ns)); err != nil {
return nil, err
}
@@ -426,14 +480,8 @@ func (s *Server) GetManifestsWithFiles(stream application.ApplicationService_Get
return fmt.Errorf("invalid request: application name is missing")
}
appName := query.GetName()
appNs := s.appNamespaceOrDefault(query.GetAppNamespace())
a, err := s.appLister.Applications(appNs).Get(appName)
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, query.GetAppNamespace(), query.GetName())
if err != nil {
return fmt.Errorf("error getting application: %w", err)
}
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil {
return err
}
@@ -538,14 +586,8 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*app
// 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(appNs).Get(ctx, appName, metav1.GetOptions{
ResourceVersion: q.GetResourceVersion(),
})
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, appNs, 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, a.RBACName(s.ns)); err != nil {
return nil, err
}
@@ -627,13 +669,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) {
appName := q.GetName()
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
a, err := s.appLister.Applications(appNs).Get(appName)
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetAppNamespace(), 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, a.RBACName(s.ns)); err != nil {
return nil, err
}
@@ -694,13 +731,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(newApp.Namespace).Get(ctx, newApp.Name, metav1.GetOptions{})
app, err := s.getApplicationEnforceRBACClient(ctx, action, newApp.Namespace, newApp.Name, "")
if err != nil {
return nil, fmt.Errorf("error getting application: %w", err)
return nil, err
}
err = s.validateAndNormalizeApp(ctx, newApp, validate)
@@ -807,7 +844,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
@@ -815,13 +852,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")
}
appName := q.GetName()
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
a, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{})
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionUpdate, q.GetAppNamespace(), 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, a.RBACName(s.ns)); err != nil {
return nil, err
}
@@ -830,7 +862,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)
}
@@ -839,11 +871,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) {
appName := q.GetName()
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
app, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{})
app, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, q.GetAppNamespace(), 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, app.RBACName(s.ns)); err != nil {
@@ -881,16 +911,16 @@ 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) {
appName := q.GetName()
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
a, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{})
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, appNs, appName, "")
if err != nil {
return nil, fmt.Errorf("error getting application: %w", err)
return nil, err
}
s.projectLock.RLock(a.Spec.Project)
@@ -956,8 +986,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())
@@ -1034,7 +1064,9 @@ func (s *Server) validateAndNormalizeApp(ctx context.Context, app *appv1.Applica
proj, err := argo.GetAppProject(app, 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)
}
@@ -1138,22 +1170,16 @@ func (s *Server) getAppResources(ctx context.Context, a *appv1.Application) (*ap
return s.cache.GetAppResourcesTree(a.InstanceName(s.ns), &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) {
appName := q.GetName()
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
a, err := s.appLister.Applications(appNs).Get(appName)
a, err := s.getApplicationEnforceRBACInformer(ctx, action, q.GetAppNamespace(), 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, a.RBACName(s.ns)); 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)
@@ -1173,7 +1199,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
@@ -1220,9 +1246,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, a.RBACName(s.ns)); err != nil {
return nil, err
}
@@ -1234,6 +1257,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)
@@ -1262,9 +1288,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, a.RBACName(s.ns)); err != nil {
return nil, err
}
var deleteOption metav1.DeleteOptions
@@ -1288,13 +1311,8 @@ func (s *Server) DeleteResource(ctx context.Context, q *application.ApplicationR
}
func (s *Server) ResourceTree(ctx context.Context, q *application.ResourcesQuery) (*appv1.ApplicationTree, error) {
appName := q.GetApplicationName()
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
a, err := s.appLister.Applications(appNs).Get(appName)
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetAppNamespace(), 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, a.RBACName(s.ns)); err != nil {
return nil, err
}
@@ -1302,14 +1320,8 @@ func (s *Server) ResourceTree(ctx context.Context, q *application.ResourcesQuery
}
func (s *Server) WatchResourceTree(q *application.ResourcesQuery, ws application.ApplicationService_WatchResourceTreeServer) error {
appName := q.GetApplicationName()
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
a, err := s.appLister.Applications(appNs).Get(appName)
_, err := s.getApplicationEnforceRBACInformer(ws.Context(), rbacpolicy.ActionGet, q.GetAppNamespace(), 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, a.RBACName(s.ns)); err != nil {
return err
}
@@ -1324,13 +1336,8 @@ func (s *Server) WatchResourceTree(q *application.ResourcesQuery, ws application
}
func (s *Server) RevisionMetadata(ctx context.Context, q *application.RevisionMetadataQuery) (*appv1.RevisionMetadata, error) {
appName := q.GetName()
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
a, err := s.appLister.Applications(appNs).Get(appName)
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetAppNamespace(), 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, a.RBACName(s.ns)); err != nil {
return nil, err
}
@@ -1365,14 +1372,9 @@ func isMatchingResource(q *application.ResourcesQuery, key kube.ResourceKey) boo
}
func (s *Server) ManagedResources(ctx context.Context, q *application.ResourcesQuery) (*application.ManagedResourcesResponse, error) {
appName := q.GetApplicationName()
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
a, err := s.appLister.Applications(appNs).Get(appName)
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetAppNamespace(), 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, a.RBACName(s.ns)); err != nil {
return nil, fmt.Errorf("error verifying rbac: %w", err)
return nil, err
}
items := make([]*appv1.ResourceDiff, 0)
@@ -1380,7 +1382,7 @@ func (s *Server) ManagedResources(ctx context.Context, q *application.ResourcesQ
return s.cache.GetAppManagedResources(a.InstanceName(s.ns), &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 {
@@ -1427,14 +1429,8 @@ func (s *Server) PodLogs(q *application.ApplicationPodLogsQuery, ws application.
}
}
appName := q.GetName()
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
a, err := s.appLister.Applications(appNs).Get(appName)
a, err := s.getApplicationEnforceRBACInformer(ws.Context(), rbacpolicy.ActionGet, q.GetAppNamespace(), 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, a.RBACName(s.ns)); err != nil {
return err
}
@@ -1625,12 +1621,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) {
appName := syncReq.GetName()
appNs := s.appNamespaceOrDefault(syncReq.GetAppNamespace())
appIf := s.appclientset.ArgoprojV1alpha1().Applications(appNs)
a, err := appIf.Get(ctx, appName, metav1.GetOptions{})
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, syncReq.GetAppNamespace(), syncReq.GetName(), "")
if err != nil {
return nil, fmt.Errorf("error getting application by name: %w", err)
return nil, err
}
proj, err := argo.GetAppProject(a, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx)
@@ -1717,6 +1710,9 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR
op.Retry = *retry
}
appName := syncReq.GetName()
appNs := s.appNamespaceOrDefault(syncReq.GetAppNamespace())
appIf := s.appclientset.ArgoprojV1alpha1().Applications(appNs)
a, err = argo.SetAppOperation(appIf, appName, &op)
if err != nil {
return nil, fmt.Errorf("error setting app operation: %w", err)
@@ -1734,14 +1730,8 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR
}
func (s *Server) Rollback(ctx context.Context, rollbackReq *application.ApplicationRollbackRequest) (*appv1.Application, error) {
appName := rollbackReq.GetName()
appNs := s.appNamespaceOrDefault(rollbackReq.GetAppNamespace())
appIf := s.appclientset.ArgoprojV1alpha1().Applications(appNs)
a, err := appIf.Get(ctx, appName, metav1.GetOptions{})
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionSync, rollbackReq.GetAppNamespace(), 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, a.RBACName(s.ns)); err != nil {
return nil, err
}
@@ -1787,6 +1777,9 @@ func (s *Server) Rollback(ctx context.Context, rollbackReq *application.Applicat
},
InitiatedBy: appv1.OperationInitiator{Username: session.Username(ctx)},
}
appName := rollbackReq.GetName()
appNs := s.appNamespaceOrDefault(rollbackReq.GetAppNamespace())
appIf := s.appclientset.ArgoprojV1alpha1().Applications(appNs)
a, err = argo.SetAppOperation(appIf, appName, &op)
if err != nil {
return nil, fmt.Errorf("error setting app operation: %w", err)
@@ -1796,24 +1789,9 @@ func (s *Server) Rollback(ctx context.Context, rollbackReq *application.Applicat
}
func (s *Server) ListLinks(ctx context.Context, req *application.ListAppLinksRequest) (*application.LinksResponse, error) {
appName := req.GetName()
appNs := s.appNamespaceOrDefault(req.GetNamespace())
a, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{})
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionSync, req.GetNamespace(), req.GetName(), "")
if err != nil {
log.WithFields(map[string]interface{}{
"application": appName,
"ns": appNs,
}).Errorf("failed to get application, error=%v", err.Error())
return nil, fmt.Errorf("error getting application")
}
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil {
log.WithFields(map[string]interface{}{
"application": appName,
"ns": appNs,
}).Warnf("unauthorized access to app, error=%v", err.Error())
return nil, fmt.Errorf("error getting application")
return nil, err
}
obj, err := kube.ToUnstructured(a)
@@ -1895,11 +1873,8 @@ func (s *Server) resolveRevision(ctx context.Context, app *appv1.Application, sy
func (s *Server) TerminateOperation(ctx context.Context, termOpReq *application.OperationTerminateRequest) (*application.OperationTerminateResponse, error) {
appName := termOpReq.GetName()
appNs := s.appNamespaceOrDefault(termOpReq.GetAppNamespace())
a, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{})
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionSync, appNs, 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, a.RBACName(s.ns)); err != nil {
return nil, err
}
@@ -1971,10 +1946,9 @@ func (s *Server) ListResourceActions(ctx context.Context, q *application.Applica
func (s *Server) getUnstructuredLiveResourceOrApp(ctx context.Context, rbacRequest string, q *application.ApplicationResourceRequest) (obj *unstructured.Unstructured, res *appv1.ResourceNode, app *appv1.Application, config *rest.Config, err error) {
if q.GetKind() == "Application" && q.GetGroup() == "argoproj.io" && q.GetName() == q.GetResourceName() {
namespace := s.appNamespaceOrDefault(q.GetAppNamespace())
app, err = s.appLister.Applications(namespace).Get(q.GetName())
app, err = s.getApplicationEnforceRBACInformer(ctx, rbacRequest, q.GetAppNamespace(), q.GetName())
if err != nil {
return nil, nil, nil, nil, fmt.Errorf("error getting app by name: %w", err)
return nil, nil, nil, nil, err
}
if err = s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacRequest, app.RBACName(s.ns)); err != nil {
return nil, nil, nil, nil, err
@@ -1987,7 +1961,7 @@ func (s *Server) getUnstructuredLiveResourceOrApp(ctx context.Context, rbacReque
} else {
res, config, app, err = s.getAppLiveResource(ctx, rbacRequest, q)
if err != nil {
return nil, nil, nil, nil, fmt.Errorf("error getting app live resource: %w", err)
return nil, nil, nil, nil, err
}
obj, err = s.kubectl.GetResource(ctx, config, res.GroupKindVersion(), res.Name, res.Namespace)
}
@@ -2157,15 +2131,8 @@ func (s *Server) plugins() ([]*appv1.ConfigManagementPlugin, error) {
}
func (s *Server) GetApplicationSyncWindows(ctx context.Context, q *application.ApplicationSyncWindowsQuery) (*application.ApplicationSyncWindowsResponse, error) {
appName := q.GetName()
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
appIf := s.appclientset.ArgoprojV1alpha1().Applications(appNs)
a, err := appIf.Get(ctx, appName, metav1.GetOptions{})
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, q.GetAppNamespace(), 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, a.RBACName(s.ns)); err != nil {
return nil, err
}
@@ -2245,3 +2212,12 @@ func (s *Server) appNamespaceOrDefault(appNs string) string {
func (s *Server) isNamespaceEnabled(namespace string) bool {
return security.IsNamespaceEnabled(namespace, s.ns, s.enabledNamespaces)
}
// 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

@@ -29,6 +29,8 @@ message ApplicationQuery {
optional string repo = 6;
// the application's namespace
optional string appNamespace = 7;
// the project names to restrict returned list applications (legacy name for backwards-compatibility)
repeated string project = 8;
}
message NodeQuery {

View File

@@ -4,12 +4,15 @@ import (
"context"
coreerrors "errors"
"fmt"
"io"
"strconv"
"sync/atomic"
"testing"
"time"
"github.com/argoproj/gitops-engine/pkg/health"
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"
@@ -18,13 +21,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"
@@ -36,6 +43,7 @@ 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"
@@ -98,6 +106,11 @@ 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)
mockWithFilesClient := &mocks.RepoServerService_GenerateManifestWithFilesClient{}
mockWithFilesClient.On("Send", mock.Anything).Return(nil)
mockWithFilesClient.On("CloseAndRecv").Return(&apiclient.ManifestResponse{}, nil)
mockRepoServiceClient.On("GenerateManifestWithFiles", mock.Anything, mock.Anything).Return(mockWithFilesClient, nil)
if isHelm {
mockRepoServiceClient.On("ResolveRevision", mock.Anything, mock.Anything).Return(fakeResolveRevesionResponseHelm(), nil)
@@ -109,15 +122,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,
@@ -202,15 +215,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(),
appInformer,
broadcaster,
mockRepoClient,
nil,
&kubetest.MockKubectlCmd{},
appCache,
kubectl,
db,
enforcer,
sync.NewKeyLock(),
@@ -301,8 +382,423 @@ func createTestApp(testApp string, opts ...func(app *appsv1.Application)) *appsv
return &app
}
type TestServerStream struct {
ctx context.Context
appName string
headerSent bool
}
func (t *TestServerStream) SetHeader(metadata.MD) error {
return nil
}
func (t *TestServerStream) SendHeader(metadata.MD) error {
return nil
}
func (t *TestServerStream) SetTrailer(metadata.MD) {}
func (t *TestServerStream) Context() context.Context {
return t.ctx
}
func (t *TestServerStream) SendMsg(m interface{}) error {
return nil
}
func (t *TestServerStream) RecvMsg(m interface{}) error {
return nil
}
func (t *TestServerStream) SendAndClose(r *apiclient.ManifestResponse) error {
return nil
}
func (t *TestServerStream) Recv() (*application.ApplicationManifestQueryWithFilesWrapper, error) {
if !t.headerSent {
t.headerSent = true
return &application.ApplicationManifestQueryWithFilesWrapper{Part: &application.ApplicationManifestQueryWithFilesWrapper_Query{
Query: &application.ApplicationManifestQueryWithFiles{
Name: pointer.String(t.appName),
Checksum: pointer.String(""),
},
}}, nil
}
return nil, io.EOF
}
func (t *TestServerStream) ServerStream() TestServerStream {
return TestServerStream{}
}
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("GetManifestsWithFiles", func(t *testing.T) {
err := appServer.GetManifestsWithFiles(&TestServerStream{ctx: adminCtx, appName: "test"})
assert.NoError(t, err)
err = appServer.GetManifestsWithFiles(&TestServerStream{ctx: noRoleCtx, appName: "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.GetManifestsWithFiles(&TestServerStream{ctx: adminCtx, appName: "does-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")
})
t.Run("ListLinks", func(t *testing.T) {
_, err := appServer.ListLinks(adminCtx, &application.ListAppLinksRequest{Name: pointer.String("test")})
assert.NoError(t, err)
_, err = appServer.ListLinks(noRoleCtx, &application.ListAppLinksRequest{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.ListLinks(adminCtx, &application.ListAppLinksRequest{Name: pointer.String("does-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("ListResourceLinks", func(t *testing.T) {
_, err := appServer.ListResourceLinks(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.ListResourceLinks(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.ListResourceLinks(adminCtx, &application.ApplicationResourceRequest{Name: pointer.String("does-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")
})
// 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 TestListAppsInNamespaceWithLabels(t *testing.T) {
appServer := newTestAppServer(newTestApp(func(app *appsv1.Application) {
appServer := newTestAppServer(t, newTestApp(func(app *appsv1.Application) {
app.Name = "App1"
app.ObjectMeta.Namespace = "test-namespace"
app.SetLabels(map[string]string{"key1": "value1", "key2": "value1"})
@@ -323,7 +819,7 @@ func TestListAppsInNamespaceWithLabels(t *testing.T) {
}
func TestListAppsInDefaultNSWithLabels(t *testing.T) {
appServer := newTestAppServer(newTestApp(func(app *appsv1.Application) {
appServer := newTestAppServer(t, newTestApp(func(app *appsv1.Application) {
app.Name = "App1"
app.SetLabels(map[string]string{"key1": "value1", "key2": "value1"})
}), newTestApp(func(app *appsv1.Application) {
@@ -401,8 +897,59 @@ func testListAppsWithLabels(t *testing.T, appQuery application.ApplicationQuery,
}
}
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"
@@ -450,7 +997,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{})
@@ -464,7 +1011,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,
@@ -477,7 +1024,7 @@ func TestCreateApp(t *testing.T) {
}
func TestCreateAppWithDestName(t *testing.T) {
appServer := newTestAppServer()
appServer := newTestAppServer(t)
testApp := newTestAppWithDestName()
createReq := application.ApplicationCreateRequest{
Application: testApp,
@@ -490,7 +1037,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,
@@ -501,7 +1048,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,
@@ -516,7 +1063,7 @@ func TestUpdateAppSpec(t *testing.T) {
func TestDeleteApp(t *testing.T) {
ctx := context.Background()
appServer := newTestAppServer()
appServer := newTestAppServer(t)
createReq := application.ApplicationCreateRequest{
Application: newTestApp(),
}
@@ -604,20 +1151,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{
@@ -657,7 +1193,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 = ""
@@ -681,7 +1217,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"
@@ -714,7 +1250,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,
@@ -734,56 +1270,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) {
@@ -791,7 +1334,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")})
@@ -816,7 +1359,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{
@@ -829,7 +1372,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)
@@ -838,7 +1381,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)
@@ -847,7 +1390,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")
@@ -865,7 +1408,7 @@ func TestGetCachedAppState(t *testing.T) {
Namespace: testNamespace,
},
}
appServer := newTestAppServer(testApp, testProj)
appServer := newTestAppServer(t, testApp, testProj)
fakeClientSet := appServer.appclientset.(*apps.Clientset)
fakeClientSet.AddReactor("get", "applications", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
return true, &appsv1.Application{Spec: appsv1.ApplicationSpec{Source: &appsv1.ApplicationSource{}}}, nil
@@ -1044,7 +1587,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
@@ -1072,7 +1615,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{}
@@ -1122,7 +1665,7 @@ func TestInferResourcesStatusHealth(t *testing.T) {
Name: "guestbook-stateful",
Namespace: "default",
}}
appServer := newTestAppServer(testApp)
appServer := newTestAppServer(t, testApp)
appStateCache := appstate.NewCache(cacheClient, time.Minute)
err := appStateCache.SetAppResourcesTree(testApp.Name, &appsv1.ApplicationTree{Nodes: []appsv1.ResourceNode{{
ResourceRef: appsv1.ResourceRef{

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

@@ -469,8 +469,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)
@@ -613,8 +614,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.
}
}
}
@@ -768,6 +769,7 @@ func newArgoCDServiceSet(a *ArgoCDServer) *ArgoCDServiceSet {
a.AppClientset,
a.appLister,
a.appInformer,
nil,
a.RepoClientset,
a.Cache,
kubectl,
@@ -945,7 +947,8 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl
// Webhook handler for git events (Note: cache timeouts are hardcoded because API server does not write to cache and not really using them)
argoDB := db.NewDB(a.Namespace, a.settingsMgr, a.KubeClientset)
acdWebhookHandler := webhook.NewHandler(a.Namespace, a.AppClientset, a.settings, a.settingsMgr, repocache.NewCache(a.Cache.GetCache(), 24*time.Hour, 3*time.Minute), a.Cache, argoDB)
acdWebhookHandler := webhook.NewHandler(a.Namespace, a.ArgoCDServerOpts.ApplicationNamespaces, a.AppClientset, a.settings, a.settingsMgr, repocache.NewCache(a.Cache.GetCache(), 24*time.Hour, 3*time.Minute), a.Cache, argoDB)
mux.HandleFunc("/api/webhook", acdWebhookHandler.Handler)
// Serve cli binaries directly from API server

View File

@@ -1,4 +1,4 @@
FROM docker.io/library/redis:7.0.5 as redis
FROM docker.io/library/redis:7.0.11-alpine as redis
# There are libraries we will want to copy from here in the final stage of the
# build, but the COPY directive does not have a way to determine system

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} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}"
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} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} "
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.35.3 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} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}"
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} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} "
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

@@ -429,7 +429,9 @@ func TestNamespacedInvalidAppProject(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 TestNamespacedAppDeletion(t *testing.T) {

View File

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

@@ -156,7 +156,7 @@ func TestCustomToolWithEnv(t *testing.T) {
})
}
//make sure we can sync and diff with --local
// make sure we can sync and diff with --local
func TestCustomToolSyncAndDiffLocal(t *testing.T) {
ctx := Given(t)
ctx.
@@ -203,7 +203,7 @@ func startCMPServer(configFile string) {
FailOnErr(RunWithStdin("", "", "../../dist/argocd", "--config-dir-path", configFile))
}
//Discover by fileName
// Discover by fileName
func TestCMPDiscoverWithFileName(t *testing.T) {
pluginName := "cmp-fileName"
Given(t).
@@ -212,7 +212,7 @@ func TestCMPDiscoverWithFileName(t *testing.T) {
time.Sleep(1 * time.Second)
os.Setenv("ARGOCD_BINARY_NAME", "argocd")
}).
Path(pluginName).
Path(pluginName + "/subdir").
When().
CreateApp().
Sync().
@@ -222,7 +222,7 @@ func TestCMPDiscoverWithFileName(t *testing.T) {
Expect(HealthIs(health.HealthStatusHealthy))
}
//Discover by Find glob
// Discover by Find glob
func TestCMPDiscoverWithFindGlob(t *testing.T) {
Given(t).
And(func() {
@@ -240,7 +240,7 @@ func TestCMPDiscoverWithFindGlob(t *testing.T) {
Expect(HealthIs(health.HealthStatusHealthy))
}
//Discover by Plugin Name
// Discover by Plugin Name
func TestCMPDiscoverWithPluginName(t *testing.T) {
Given(t).
And(func() {
@@ -261,7 +261,7 @@ func TestCMPDiscoverWithPluginName(t *testing.T) {
Expect(HealthIs(health.HealthStatusHealthy))
}
//Discover by Find command
// Discover by Find command
func TestCMPDiscoverWithFindCommandWithEnv(t *testing.T) {
pluginName := "cmp-find-command"
ctx := Given(t)
@@ -329,3 +329,54 @@ func TestPruneResourceFromCMP(t *testing.T) {
assert.Error(t, err)
})
}
func TestCMPWithSymlinkPartialFiles(t *testing.T) {
Given(t, WithTestData("testdata2")).
And(func() {
go startCMPServer("./testdata2/cmp-symlink")
time.Sleep(1 * time.Second)
os.Setenv("ARGOCD_BINARY_NAME", "argocd")
}).
Path("guestbook-partial-symlink-files").
When().
CreateApp().
Sync().
Then().
Expect(OperationPhaseIs(OperationSucceeded)).
Expect(SyncStatusIs(SyncStatusCodeSynced)).
Expect(HealthIs(health.HealthStatusHealthy))
}
func TestCMPWithSymlinkFiles(t *testing.T) {
Given(t, WithTestData("testdata2")).
And(func() {
go startCMPServer("./testdata2/cmp-symlink")
time.Sleep(1 * time.Second)
os.Setenv("ARGOCD_BINARY_NAME", "argocd")
}).
Path("guestbook-symlink-files").
When().
CreateApp().
Sync().
Then().
Expect(OperationPhaseIs(OperationSucceeded)).
Expect(SyncStatusIs(SyncStatusCodeSynced)).
Expect(HealthIs(health.HealthStatusHealthy))
}
func TestCMPWithSymlinkFolder(t *testing.T) {
Given(t, WithTestData("testdata2")).
And(func() {
go startCMPServer("./testdata2/cmp-symlink")
time.Sleep(1 * time.Second)
os.Setenv("ARGOCD_BINARY_NAME", "argocd")
}).
Path("guestbook-symlink-folder").
When().
CreateApp().
Sync().
Then().
Expect(OperationPhaseIs(OperationSucceeded)).
Expect(SyncStatusIs(SyncStatusCodeSynced)).
Expect(HealthIs(health.HealthStatusHealthy))
}

View File

@@ -50,8 +50,8 @@ type ContextArgs struct {
AppNamespace string
}
func Given(t *testing.T) *Context {
fixture.EnsureCleanState(t)
func Given(t *testing.T, opts ...fixture.TestOption) *Context {
fixture.EnsureCleanState(t, opts...)
return GivenWithSameState(t)
}

View File

@@ -514,7 +514,30 @@ func SetParamInNotificationsConfigMap(key, value string) {
})
}
func EnsureCleanState(t *testing.T) {
type TestOption func(option *testOption)
type testOption struct {
testdata string
}
func newTestOption(opts ...TestOption) *testOption {
to := &testOption{
testdata: "testdata",
}
for _, opt := range opts {
opt(to)
}
return to
}
func WithTestData(testdata string) TestOption {
return func(option *testOption) {
option.testdata = testdata
}
}
func EnsureCleanState(t *testing.T, opts ...TestOption) {
opt := newTestOption(opts...)
// In large scenarios, we can skip tests that already run
SkipIfAlreadyRun(t)
// Register this test after it has been run & was successfull
@@ -632,7 +655,7 @@ func EnsureCleanState(t *testing.T) {
}
// set-up tmp repo, must have unique name
FailOnErr(Run("", "cp", "-Rf", "testdata", repoDirectory()))
FailOnErr(Run("", "cp", "-Rf", opt.testdata, repoDirectory()))
FailOnErr(Run(repoDirectory(), "chmod", "777", "."))
FailOnErr(Run(repoDirectory(), "git", "init"))
FailOnErr(Run(repoDirectory(), "git", "add", "."))
@@ -811,6 +834,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

@@ -7,4 +7,4 @@ spec:
generate:
command: [sh, -c, 'echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"Foo\": \"$FOO\", \"KubeVersion\": \"$KUBE_VERSION\", \"KubeApiVersion\": \"$KUBE_API_VERSIONS\",\"Bar\": \"baz\"}}}"']
discover:
fileName: "subdir/s*.yaml"
fileName: "cmp-fileName/subdir/s*.yaml"

View File

@@ -0,0 +1,13 @@
apiVersion: argoproj.io/v1alpha1
kind: ConfigManagementPlugin
metadata:
name: cmp-symlink
spec:
version: v1.0
init:
command: [kustomize, version]
generate:
command: [sh, -c, 'kustomize edit set image test=quay.io/argoprojlabs/argocd-e2e-container:0.2 && kustomize build --load-restrictor LoadRestrictionsNone']
discover:
find:
glob: "**/kustomization.yaml"

View File

@@ -0,0 +1 @@
../guestbook/guestbook-ui-deployment.yaml

View File

@@ -0,0 +1,10 @@
apiVersion: v1
kind: Service
metadata:
name: guestbook-ui
spec:
ports:
- port: 80
targetPort: 80
selector:
app: guestbook-ui

View File

@@ -0,0 +1,6 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ./guestbook-ui-deployment.yaml
- ./guestbook-ui-svc.yaml

View File

@@ -0,0 +1 @@
../guestbook/guestbook-ui-deployment.yaml

View File

@@ -0,0 +1 @@
../guestbook/guestbook-ui-svc.yaml

View File

@@ -0,0 +1 @@
../guestbook/kustomization.yaml

View File

@@ -0,0 +1 @@
guestbook

View File

@@ -0,0 +1,23 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: guestbook-ui
labels:
test: "true"
spec:
replicas: 0
revisionHistoryLimit: 3
selector:
matchLabels:
app: guestbook-ui
template:
metadata:
labels:
app: guestbook-ui
spec:
containers:
- image: test
imagePullPolicy: IfNotPresent
name: guestbook-ui
ports:
- containerPort: 80

View File

@@ -0,0 +1,10 @@
apiVersion: v1
kind: Service
metadata:
name: guestbook-ui
spec:
ports:
- port: 80
targetPort: 80
selector:
app: guestbook-ui

Some files were not shown because too many files have changed in this diff Show More