Compare commits

..

136 Commits

Author SHA1 Message Date
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
argo-bot
e05298b9c6 Bump version to 2.6.3 2023-02-27 14:27:28 +00:00
argo-bot
57c30b5e04 Bump version to 2.6.3 2023-02-27 14:27:24 +00:00
gcp-cherry-pick-bot[bot]
8e544cbb97 fix: traverse generator tree when getting requeue time (#12407) (#12612)
* add unit test reproducing




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



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

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



* correct path



* missed a spot



---------




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

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



* Retrigger CI pipeline



---------




* fix: traverse generator tree when getting requeue time



* fix: traverse generator tree when getting requeue time



* remove duplicate code



* Retrigger CI pipeline



* revert gitignore



* update from code review



---------

Signed-off-by: rumstead <rjumstead@gmail.com>
Signed-off-by: rumstead <37445536+rumstead@users.noreply.github.com>
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Signed-off-by: Joshua Helton <jdoghelton@gmail.com>
Co-authored-by: rumstead <37445536+rumstead@users.noreply.github.com>
Co-authored-by: Remington Breeze <remington@breeze.software>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: jphelton <jdoghelton@gmail.com>
2023-02-24 16:31:08 -05:00
gcp-cherry-pick-bot[bot]
a5fb6ffe6b fix: evaluate all possible refresh reasons for multi-source apps (#12379) (#12609)
* fix: evaluate all possible refresh reasons for multi-source apps (#12379)



* remove redundant parentheses



* tests



* don't auto-sync, it makes tests flaky



* auto-sync because sync CLI doesn't work for multi-source apps



* don't require out-of-sync - app may sync quickly



* timeout 60



* fix timeout



---------

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-02-24 13:39:40 -05:00
Michael Crenshaw
691f20461e docs: add 2.5->2.6 upgrade notes to table of contents (#12319)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-02-17 17:03:26 -05:00
Justin Marquis
7756a816ab chore: use registry.k8s.io instead of k8s.gcr.io (#12362)
Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-02-17 15:13:38 -05:00
Nicholas Morey
fd4c5f694a docs: add multi source app ref in helm section (#12499)
Signed-off-by: Nicholas Morey <nicholas@morey.tech>
2023-02-17 15:04:44 -05:00
dependabot[bot]
f148fa42a1 chore(deps): bump imjasonh/setup-crane from 0.2 to 0.3 (#12504)
Bumps [imjasonh/setup-crane](https://github.com/imjasonh/setup-crane) from 0.2 to 0.3.
- [Release notes](https://github.com/imjasonh/setup-crane/releases)
- [Commits](e82f1b9a80...00c9e93efa)

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

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

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

* docs: add FAQ entry

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

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

---------

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

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

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

Signed-off-by: mdsjip <2284562+mdsjip@users.noreply.github.com>
2023-02-17 14:11:16 -05:00
Steve Ramage
21cdcc124d docs: fix a few typos and grammar mistakes (#12280)
Signed-off-by: Steve Ramage <commits@sjrx.net>
Co-authored-by: Steve Ramage <commits@sjrx.net>
2023-02-17 14:07:07 -05:00
dependabot[bot]
d195568c90 chore(deps): bump docker/setup-buildx-action from 2.4.0 to 2.4.1 (#12308)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2.4.0 to 2.4.1.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](15c905b16b...f03ac48505)

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

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-17 13:52:30 -05:00
Vaibhav Bhembre
9c0ca76d2e fix: setting spec.SyncPolicy crashes 'argocd appset get' output (#12424) (#12425)
Signed-off-by: Vaibhav Bhembre <vaibhav@digitalocean.com>
2023-02-17 13:50:57 -05:00
Wojtek Cichoń
0d0570de53 docs: Updated link to Jenkins and added GitHub Actions link (#12465)
Signed-off-by: Wojtek Cichoń <wojtek.cichon@protonmail.com>
2023-02-17 13:36:26 -05:00
Zadkiel Aharonian
a731a38026 docs: fix typo in health documentation (#12497)
Signed-off-by: Zadkiel Aharonian <hello@zadkiel.fr>
2023-02-17 13:34:23 -05:00
Saumeya Katyal
03d1c052f6 fix(security): add url validation for help chat (#9956) (#10417)
* fix: add url validation for help chat

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

* lint check

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

* lint fix

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

* review comments

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

---------

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

The wording of this warning was confusing.

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

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

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

---------

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

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

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

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

* fixing lint

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

* Update application-resource-list.tsx

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

* fix for  lint error

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

---------

Signed-off-by: schakradari <saisindhu_chakradari@intuit.com>
Signed-off-by: schakrad <58915923+schakrad@users.noreply.github.com>
2023-02-17 11:57:52 -05:00
jphelton
7bbedff9da fix: when resource does not exist node menu and resource details shou… (#12360)
* fix: when resource does not exist node menu and resource details should still render

Signed-off-by: Joshua Helton <jdoghelton@gmail.com>

* Retrigger CI pipeline

Signed-off-by: Joshua Helton <jdoghelton@gmail.com>

---------

Signed-off-by: Joshua Helton <jdoghelton@gmail.com>
2023-02-16 09:06:43 -08:00
argo-bot
6e02f8b232 Bump version to 2.6.2 2023-02-16 14:52:52 +00:00
argo-bot
e2b280c3dd Bump version to 2.6.2 2023-02-16 14:52:45 +00:00
Michael Crenshaw
74726cf11e Merge pull request from GHSA-3jfq-742w-xg8j
fix test name

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

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

* correct path

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

* missed a spot

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

---------

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-02-09 12:18:06 -05:00
argo-bot
3f143c9307 Bump version to 2.6.1 2023-02-08 18:35:36 +00:00
argo-bot
7fadddcba9 Bump version to 2.6.1 2023-02-08 18:35:30 +00:00
Michael Crenshaw
cac327cf64 fix: sanitize repo creds in error messages (#12309) (#12320)
* fix: sanitize repo creds in error messages (#12309)

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

* simplify

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

* comment

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

* include error message

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

---------

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-02-08 10:48:03 -05:00
argo-bot
acc554f3d9 Bump version to 2.6.0 2023-02-06 21:02:03 +00:00
argo-bot
f91b9f45ab Bump version to 2.6.0 2023-02-06 21:01:58 +00:00
Michael Crenshaw
893569aab4 docs: update config management plugin docs (#11690)
* docs: update config management plugin docs

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

* update plugin name/discovery section

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

* don't remove client-side gen

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

* fixes

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

---------

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-02-06 16:01:06 -05:00
Michael Crenshaw
17e0875ed7 fix: remove argocd-cm plugin warnings (#11740)
* chore: remove argocd-cm plugin warnings

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

* more fixes

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

* fix lint

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

---------

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-02-06 14:50:18 -05:00
Michael Crenshaw
1c343837e9 docs: add 2.5->2.6 upgrade notes to list (#12283)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-02-06 13:20:06 -05:00
Michael Crenshaw
0ce5183857 fix: multi-source app refresh (#11772) (#12217)
* fix multi-source refresh

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

* serialize nil and empty resolvedRevisions the same to avoid cache misses

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

* more consistent naming

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

* document duplication

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

* add todo

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

---------

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-02-06 13:02:25 -05:00
Michael Crenshaw
70669be1cc chore: rename 'progressive rollouts' to 'progressive syncs' (#12265)
* chore: rename 'progressive rollouts' to 'progressive syncs'

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

* rename docs file

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

* Update cmd/argocd-applicationset-controller/commands/applicationset_controller.go

Co-authored-by: Panagiotis Georgiadis <pgeorgia@redhat.com>
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

---------

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: Panagiotis Georgiadis <pgeorgia@redhat.com>
2023-02-06 11:10:50 -05:00
Jake
212b3948dd fix: don't dump SSG to server logs, change deprecation notice to v2.7 (#12285)
* fix: don't dump SSG to server logs, change deprecation notice to v2.7

Signed-off-by: notfromstatefarm <86763948+notfromstatefarm@users.noreply.github.com>

* Update server/server.go

Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Signed-off-by: Jake <86763948+notfromstatefarm@users.noreply.github.com>

---------

Signed-off-by: notfromstatefarm <86763948+notfromstatefarm@users.noreply.github.com>
Signed-off-by: Jake <86763948+notfromstatefarm@users.noreply.github.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-02-03 13:32:45 -05:00
Michael Crenshaw
8074e6f9cc chore: upgrade cookiejar to avoid CVE-2022-25901 (#12030)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-02-03 10:42:08 -05:00
Keith Chong
a951ccacf0 docs: sign up for 1.8 release (#12266)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-02-03 10:34:15 -05:00
Michael Crenshaw
119116258b docs: release cycle refresh (#11137)
* docs: release cycle

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

* remove TODOs

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

* add release champion

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

* formatting

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

* no 2.6 champion yet

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

* fix dates

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

* checklist links

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

* reorg

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

* reuse roadmap doc, add note about Release Champion access requirements

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

* note triage access requirement

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

* release issue template

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

* tweaks

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

* simplify

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

* update dates

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

* add notes for next release champion

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

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-02-03 10:34:09 -05:00
Josh Soref
278fd1e175 docs: Fix heading to not include a v for the second version (#12218)
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-02-03 10:29:14 -05:00
wmgroot
9a35eb63a5 fix: AppSet Progressive sync fixes, docs, and logging improvements (#11924) (#12103)
Signed-off-by: wmgroot <wmgroot@gmail.com>
2023-02-03 10:27:40 -05:00
Ishita Sequeira
95b52df1ff fix: disable edit button on App Details Summary and Parameters tabs multi-source apps (#12048)
* disable edit button on App Details Summary and Parameters tabs for applications with multiple sources

Signed-off-by: ishitasequeira <ishiseq29@gmail.com>

* disable editing specific fields in Application Summary Tab for Multiple Source apps

Signed-off-by: ishitasequeira <ishiseq29@gmail.com>

---------

Signed-off-by: ishitasequeira <ishiseq29@gmail.com>
2023-02-03 10:14:33 -05:00
Ishita Sequeira
97761cc557 fix: return nil if reading application set was successful (#12261)
Signed-off-by: ishitasequeira <ishiseq29@gmail.com>
2023-02-03 10:05:15 -05:00
Thomas Decaux
edb95e891c docs: add destination.name example (#12242)
I had trouble finding the documentation to use the cluster name for destination, instead of the full URL. This is really useful.

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

Signed-off-by: Thomas Decaux <ebuildy@gmail.com>
Signed-off-by: ebuildy <ebuildy@gmail.com>
2023-02-02 12:56:09 -05:00
Soumya Ghosh Dastidar
1569dba5ce feat: make discovery field optional in plugins (#12073)
* feat: make discovery field optional in plugins

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>

* docs: updated plugin docs

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>

* docs: updated plugin docs

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>

* feat: updated discovery check for named plugins

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>

* fix: fixed unit tests

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>

* feat: simplified code

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>

* fix: close connection on error

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>

* feat: simplify code

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>

* feat: add named return values

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>

---------

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>
2023-02-02 12:08:22 -05:00
argo-bot
278a29c875 Bump version to 2.6.0-rc7 2023-02-02 14:56:56 +00:00
argo-bot
d0862cd275 Bump version to 2.6.0-rc7 2023-02-02 14:56:48 +00:00
James Brady
f31e1aaf4c docs: Fix list formatting in "Resource Actions" docs page (#12061)
Signed-off-by: James Brady <goodgravy@users.noreply.github.com>
2023-02-02 09:55:54 -05:00
Kostis (Codefresh)
4175d894da docs: Clarify directory recursion (#12037)
Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>
2023-02-02 09:55:35 -05:00
dependabot[bot]
4c7a024875 chore(deps): bump docker/setup-buildx-action from 2.2.1 to 2.4.0 (#12227)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2.2.1 to 2.4.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](8c0edbc76e...15c905b16b)

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

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-02 09:54:27 -05:00
Nobuo Takizawa
d44b353a4e chore: Update dex's image tag that is forgotten to be updated (#12234)
Signed-off-by: nobuyo <longzechangsheng@gmail.com>
2023-02-02 09:54:10 -05:00
Adam Jensen
0ac6d0ca5a docs: Fix copy that refers to a different CLI flag (#12236)
Signed-off-by: Adam Jensen <adam@acj.sh>
2023-02-02 09:53:53 -05:00
jannfis
70a7b9bd00 feat: Allow force of basic auth for HTTP repositories (#11983)
* feat: Allow force of basic auth for HTTP repositories

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

* Some docs

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

---------

Signed-off-by: jannfis <jann@mistrust.net>
2023-02-02 09:51:49 -05:00
Artur Rodrigues
52365f88a0 fix: repo-server - prevent locked state after unclean git checkout (#7898) (#11805)
* Pull in new version of argoproj/pkg

Signed-off-by: Artur Rodrigues <artur.rodrigues@lacework.net>

* Allow timeout behavior to be specified in util/exec/exec

Signed-off-by: Artur Rodrigues <artur.rodrigues@lacework.net>

* Git processes receive SIGTERM when timedout

Signed-off-by: Artur Rodrigues <artur.rodrigues@lacework.net>

* Update util/exec/exec_test.go

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

---------

Signed-off-by: Artur Rodrigues <artur.rodrigues@lacework.net>
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-02-02 09:51:03 -05:00
Panagiotis Georgiadis
a0b7fc5cab fix: Upgrade gopkg.in/yaml.v2 to v2.4.0 [release-2.6] (#12245)
Signed-off-by: Panagiotis Georgiadis <pgeorgia@redhat.com>
2023-02-01 16:06:35 -05:00
argo-bot
e6df2fcfca Bump version to 2.6.0-rc6 2023-01-27 23:12:41 +00:00
argo-bot
719b0aae82 Bump version to 2.6.0-rc6 2023-01-27 23:12:37 +00:00
Michael Crenshaw
9a27a20c5d feat(security): require the aud claim from OIDC providers by default (#12187)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-27 17:44:58 -05:00
Ishita Sequeira
01ef2e0c32 fix: disable More button for multiple sources (#11760) (#11971)
* disable More button for multiple sources

Signed-off-by: ishitasequeira <ishiseq29@gmail.com>

* address comments

Signed-off-by: ishitasequeira <ishiseq29@gmail.com>

* Addressed merge conflicts

Signed-off-by: ishitasequeira <ishiseq29@gmail.com>

---------

Signed-off-by: ishitasequeira <ishiseq29@gmail.com>
2023-01-27 15:27:50 -05:00
Eugen Friedland
063dce443f fix(health): Handling SparkApplication CRD health status if dynamic allocation is enabled (#7557) (#11522)
Signed-off-by: Yevgeniy Fridland <yevg.mord@gmail.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-27 15:17:05 -05:00
Michael Crenshaw
5094ee988f fix: add CLI client IDs to default OIDC allowed audiences (#12170) (#12179)
* fix(settings): add CLI client ID in default OAuth2 allowed audiences

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

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

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

* docs

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

* test

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

* handle expired token properly

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

---------

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

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

* remove context key for now

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

* implement unit-tests

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

* remove claim valid check for now

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

* remove unnecessary test

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

* fix lint

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

* don't too much details in http response

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

* Fix error

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

* Fix lint

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

* trigger build

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

* builder pattern in terminal feature-flag middleware

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

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>
2023-01-27 13:25:21 -05:00
Ian Delahorne
10caed890c fix: Add namespace to sub-application link URLs (#11946)
Signed-off-by: Ian Delahorne <ian@patreon.com>
Co-authored-by: Remington Breeze <remington@breeze.software>
2023-01-27 13:19:44 -05:00
Alexander Matyushentsev
bc88f98f6d fix: create separate API to load configured plugins (#12164)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2023-01-26 15:16:26 -08:00
argo-bot
e790028e5c Bump version to 2.6.0-rc5 2023-01-25 17:45:45 +00:00
argo-bot
6ba9975245 Bump version to 2.6.0-rc5 2023-01-25 17:45:41 +00:00
Michael Crenshaw
79baabc837 Merge pull request from GHSA-q9hr-j4rf-8fjc
* fix: verify audience claim

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

* fix lint

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

* go mod tidy

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

* handle single aud claim marshaled as a string

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

Signed-off-by: CI <350466+crenshaw-dev@users.noreply.github.com>
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: Vladimir Pouzanov <farcaller@gmail.com>
2023-01-25 09:15:03 -05:00
Dan Garfield
9b6815f06f Merge pull request from GHSA-6p4m-hw2h-6gmw
Signed-off-by: ChangZhuo Chen (陳昌倬) <czchen@czchen.org>

add test

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

better comment

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

Signed-off-by: CI <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: ChangZhuo Chen (陳昌倬) <czchen@czchen.org>
2023-01-25 09:14:29 -05:00
Mike Bryant
16f2de0b29 fix: Support resource actions for apps in different Namespace (#12115)
Signed-off-by: Mike Bryant <mike.bryant@mettle.co.uk>

Signed-off-by: Mike Bryant <mike.bryant@mettle.co.uk>
2023-01-24 17:32:35 -05:00
Justin Marquis
d744465cdf chore: disable docker sbom and attestations (#12059)
Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>

Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>
2023-01-20 10:01:45 -05:00
Nicholas Morey
240ffc30ef docs: clarify value for disabling tools (#11395)
* docs: clarify value for disabling tools

Although it is implied to set the value for the key to `false`, this explicitly states it to add clarity. Along with some wording changes.

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

* docs: add use-case for disabling tools

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

Signed-off-by: Nicholas Morey <nicholas@morey.tech>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-18 16:46:59 -05:00
Alex Eftimie
22662559fe fix: values should always be visible, even when they are empty (#11676) (#11681)
Signed-off-by: Alex Eftimie <alex.eftimie@getyourguide.com>

Signed-off-by: Alex Eftimie <alex.eftimie@getyourguide.com>
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-18 09:49:45 -05:00
argo-bot
590ea32083 Bump version to 2.6.0-rc4 2023-01-18 02:10:05 +00:00
argo-bot
f8483d2be4 Bump version to 2.6.0-rc4 2023-01-18 02:09:58 +00:00
Michael Crenshaw
25d1d7aca2 chore: upgrade net/http2 to avoid CVE-2022-41717 (#11616)
* chore: upgrade net/http2 to avoid CVE-2022-41717

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

* tidy

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

* ugprade net

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

* go mod tidy

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

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-17 18:43:26 -05:00
Aymen Ben Tanfous
678d773a6a fix: Fixed matrix requeueAfterSeconds for PR (#10914) (#10915)
* Fixed matrix requeueAfterSeconds for PR

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

* A try to make some tests

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

* Fixed default test returns the default time

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

* Fixed default test returns the default time

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

Signed-off-by: Aymen Ben Tanfous <aymen.bentanfous@gmail.com>
Signed-off-by: Aymen Ben Tanfous <aymen.bentanfous@cimpress.com>
Signed-off-by: Aymen Ben Tanfous <aymenbentanfous@gmail.com>
Co-authored-by: Aymen Ben Tanfous <aymen.bentanfous@cimpress.com>
Co-authored-by: Aymen Ben Tanfous <aymenbentanfous@gmail.com>
2023-01-17 16:54:05 -05:00
argo-bot
e51d0b3224 Bump version to 2.6.0-rc3 2023-01-13 19:46:55 +00:00
argo-bot
0f5f41ebb0 Bump version to 2.6.0-rc3 2023-01-13 19:46:50 +00:00
Michael Crenshaw
ee4b3cacc9 fix: pathParamPrefix is optional (#11796) (#11943)
* fix: pathParamPrefix is optional

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

* go mod tidy

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

* fix import

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>

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-13 13:34:32 -05:00
dependabot[bot]
62a521ccf6 chore(deps): bump actions/cache from 3.2.2 to 3.2.3 (#11928)
Bumps [actions/cache](https://github.com/actions/cache) from 3.2.2 to 3.2.3.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](4723a57e26...58c146cc91)

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

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

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

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-11 14:06:10 -05:00
reggie-k
826507897d docs: custom health check resource group wildcard (#4212)
Signed-off-by: reggie <reginakagan@gmail.com>

Signed-off-by: reggie <reginakagan@gmail.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-11 10:05:45 -05:00
dependabot[bot]
5fcebcc799 chore(deps): bump actions/setup-node from 3.5.1 to 3.6.0 (#11896)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3.5.1 to 3.6.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](8c91899e58...64ed1c7eab)

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

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

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

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-10 15:21:40 -05:00
Ryan Umstead
6ebca0bd01 fix(redis): explicit bind to redis and sentinel for IPv4 clusters (#11388) (#11862)
* fix(redis): explicit bind to redis and sentinel for IPv4 clusters #11388

Signed-off-by: rumstead <rjumstead@gmail.com>

* fix(redis): run manifests generate

Signed-off-by: rumstead <rjumstead@gmail.com>

* fix(redis): run manifests generate

Signed-off-by: rumstead <rjumstead@gmail.com>

* Retrigger CI pipeline

Signed-off-by: rumstead <rjumstead@gmail.com>

Signed-off-by: rumstead <rjumstead@gmail.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-10 14:02:14 -05:00
Michael Crenshaw
93fa2a46a5 chore: upgrade redis to 7.0.7 to avoid CVE-2022-3996 (#11925)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-10 10:36:36 -05:00
Alex Eftimie
72f92b6f2a fix: parsed url is not exposed (#11816) (#11916)
* fix: parsed url is not exposed (#11816)

Signed-off-by: Alex Eftimie <alex.eftimie@getyourguide.com>

* subtler fix, thanks @woehrl01

Signed-off-by: Alex Eftimie <alex.eftimie@getyourguide.com>

Signed-off-by: Alex Eftimie <alex.eftimie@getyourguide.com>
2023-01-10 10:34:38 -05:00
Alex Eftimie
7d54482d42 bug: fix url parsing for non git urls (oci://, no protocol etc) (#11819)
Signed-off-by: Alex Eftimie <alex.eftimie@getyourguide.com>
2023-01-10 10:34:31 -05:00
Soumya Ghosh Dastidar
fe8049fc50 docs: added deep links doc (#11888)
* docs: added deep links doc

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>

* refactor: resolved review comments

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>

* refactor: moved cmp and deep links doc to operator manual

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>

* feat: add warning for templates

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>

* feat: add note for secret data fields being redacted

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>
2023-01-10 10:28:33 -05:00
Michael Crenshaw
8065748cca fix: upgrade qs to avoid CVE-2022-24999 (#11743)
* fix: upgrade qs to avoid CVE-2022-24999

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

* don't explicitly add dependency

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

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-10 09:28:25 -05:00
dependabot[bot]
57560b32f6 chore(deps): bump actions/cache from 3.2.0 to 3.2.2 (#11839)
Bumps [actions/cache](https://github.com/actions/cache) from 3.2.0 to 3.2.2.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](c17f4bf466...4723a57e26)

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

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

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

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

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

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

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

* grammar tweaks

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

* grammar tweaks

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

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-01-09 16:31:15 -05:00
Leonardo Luz Almeida
bb8ef6dfa3 fix: web terminal namespace handler (#11891)
Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>
2023-01-09 16:28:12 -05:00
argo-bot
6a9f37ca7d Bump version to 2.6.0-rc2 2023-01-05 14:53:33 +00:00
argo-bot
b357fd61c0 Bump version to 2.6.0-rc2 2023-01-05 14:53:25 +00:00
asingh
f8d275c50d fix: Applications with suspended jobs now marked "Suspended" instead of "Progressing" (#11603) (#11626)
* fix: add suspended condition

Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com>

* fix: add suspended condition

Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com>

* Update go.sum

Signed-off-by: asingh <11219262+ashutosh16@users.noreply.github.com>

* fix: add suspended condition

Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com>

* fix: add suspended condition

Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com>

* Update go.sum

Signed-off-by: asingh <11219262+ashutosh16@users.noreply.github.com>

* upgrade notes for 2.6

Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com>

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

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-22 13:38:43 -05:00
Michael Crenshaw
f869cc4feb fix: web terminal outside argocd namespace (#11166) (#11400)
* fix: web terminal outside argocd namespace (#11166)

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

* reorganize

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

* fix reference

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

* move things around, fix stuff maybe

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>
2022-12-22 13:29:22 -05:00
Ishita Sequeira
fab4a3cb92 fix: disable rollback button for apps with multiple sources (#11785)
* disble rollback button for apps with multiple sources

Signed-off-by: ishitasequeira <ishiseq29@gmail.com>

* fix lint errors

Signed-off-by: ishitasequeira <ishiseq29@gmail.com>

* disble rollback button for apps with multiple sources

Signed-off-by: ishitasequeira <ishiseq29@gmail.com>

Signed-off-by: ishitasequeira <ishiseq29@gmail.com>
2022-12-21 12:37:39 -05:00
Chris Reilly
7dab9b23bf docs: Update bullet formatting on Progressive Rollouts.md (#11777)
The bullet list in the example format was rendering inline in the paragraph on the doc site rather than showing a bulleted list. This also makes the rest of the doc follow the same convention. 

Signed-off-by: Chris Reilly <chris@chris-reilly.com>

Signed-off-by: Chris Reilly <chris@chris-reilly.com>
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2022-12-21 08:52:26 -05:00
Ishita Sequeira
c8d010ceb0 fix: set Path as empty if path is not specified for a source in multiple sources (#11756) (#11774)
* set Path as '' if path is not specified for a source in multiple sources

Signed-off-by: ishitasequeira <ishiseq29@gmail.com>

* update check for not setting value of path

Signed-off-by: ishitasequeira <ishiseq29@gmail.com>

* cleanup

Signed-off-by: ishitasequeira <ishiseq29@gmail.com>

* address comments

Signed-off-by: ishitasequeira <ishiseq29@gmail.com>

* fix lint

Signed-off-by: ishitasequeira <ishiseq29@gmail.com>

* fix lint

Signed-off-by: ishitasequeira <ishiseq29@gmail.com>

* Update ui/src/app/shared/components/revision.tsx

Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Signed-off-by: Ishita Sequeira <46771830+ishitasequeira@users.noreply.github.com>

Signed-off-by: ishitasequeira <ishiseq29@gmail.com>
Signed-off-by: Ishita Sequeira <46771830+ishitasequeira@users.noreply.github.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2022-12-21 08:45:08 -05:00
Justin Marquis
3fa9a9197b chore: fix lint error (#11788)
Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>

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

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

* Retrigger CI pipeline

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

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

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

Signed-off-by: Gaël Jourdan-Weil <gjourdanweil@gmail.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2022-12-20 12:51:54 -05:00
toyamagu
3a8802f083 fix: sources.ref allow hyphen and underscore (#11775)
Signed-off-by: toyamagu2021@gmail.com <toyamagu2021@gmail.com>

Signed-off-by: toyamagu2021@gmail.com <toyamagu2021@gmail.com>
2022-12-20 12:50:18 -05:00
Nicholas Morey
cdaf2b2c73 fix: support for enabling progressive rollouts from argocd-cmd-params-cm (#11776)
* fix(applicationset): use consistent syntax for env vars

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

* fix(manifests): add new appset env var from configmap

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

Signed-off-by: Nicholas Morey <nicholas@morey.tech>
2022-12-20 12:48:05 -05:00
Ishita Sequeira
222cdf4711 chore: change logging level to Debug (#11773)
Signed-off-by: ishitasequeira <ishiseq29@gmail.com>

Signed-off-by: ishitasequeira <ishiseq29@gmail.com>
2022-12-20 11:38:07 -05:00
toyamagu
c58d3843d5 docs: bump elasticsearch version to 8.5.1 (#11771)
Signed-off-by: toyamagu2021@gmail.com <toyamagu2021@gmail.com>

Signed-off-by: toyamagu2021@gmail.com <toyamagu2021@gmail.com>
2022-12-20 10:20:10 -05:00
Matt Clegg
383a65fe71 docs: correct SSO configuration URL in example configmap (#11720)
Signed-off-by: Matt Clegg <m@cle.gg>

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

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

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

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

* Remove pinned kubectl version

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

* Cleaner approach to fix e2e test

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

* Fix

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

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

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

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

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

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

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

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

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

* use sha hash

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

Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>
2022-12-19 20:45:02 -05:00
argo-bot
81e40d53fe Bump version to 2.6.0-rc1 2022-12-19 16:36:27 +00:00
argo-bot
8532cfec4a Bump version to 2.6.0-rc1 2022-12-19 16:36:21 +00:00
595 changed files with 30756 additions and 46657 deletions

View File

@@ -11,17 +11,3 @@ cmd/**/debug
debug.test
coverage.out
ui/node_modules/
test-results/
test/
manifests/
hack/
docs/
examples/
.github/
!test/fixture
!test/container
!hack/installers
!hack/gpg-wrapper.sh
!hack/git-verify-wrapper.sh
!hack/tool-versions.sh
!hack/install.sh

View File

@@ -1,3 +0,0 @@
enabled: true
preservePullRequestTitle: true

View File

@@ -16,28 +16,3 @@ updates:
directory: "/ui/"
schedule:
interval: "daily"
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "docker"
directory: "/test/container/"
schedule:
interval: "daily"
- package-ecosystem: "docker"
directory: "/test/e2e/multiarch-container/"
schedule:
interval: "daily"
- package-ecosystem: "docker"
directory: "/test/remote/"
schedule:
interval: "daily"
- package-ecosystem: "docker"
directory: "/ui-test/"
schedule:
interval: "daily"

View File

@@ -11,10 +11,7 @@ Checklist:
* [ ] Does this PR require documentation updates?
* [ ] I've updated documentation as required by this PR.
* [ ] Optional. My organization is added to USERS.md.
* [ ] I have signed off all my commits as required by [DCO](https://github.com/argoproj/argoproj/blob/master/community/CONTRIBUTING.md#legal)
* [ ] I have signed off all my commits as required by [DCO](https://github.com/argoproj/argoproj/tree/master/community#contributing-to-argo)
* [ ] I have written unit and/or e2e tests for my change. PRs without these are unlikely to be merged.
* [ ] My build is green ([troubleshooting builds](https://argo-cd.readthedocs.io/en/latest/developer-guide/ci/)).
* [ ] My new feature complies with the [feature status](https://github.com/argoproj/argoproj/blob/master/community/feature-status.md) guidelines.
* [ ] I have added a brief description of why this PR is necessary and/or what this PR solves.
Please see [Contribution FAQs](https://argo-cd.readthedocs.io/en/latest/developer-guide/faq/) if you have questions about your pull-request.

View File

@@ -1,38 +0,0 @@
# Workflows
| Workflow | Description |
|--------------------|----------------------------------------------------------------|
| ci-build.yaml | Build, lint, test, codegen, build-ui, analyze, e2e-test |
| codeql.yaml | CodeQL analysis |
| image-reuse.yaml | Build, push, and Sign container images |
| image.yaml | Build container image for PR's & publish for push events |
| pr-title-check.yaml| Lint PR for semantic information |
| init-release.yaml | Build manifests and version then create a PR for release branch|
| release.yaml | Build images, cli-binaries, provenances, and post actions |
| update-snyk.yaml | Scheduled snyk reports |
# Reusable workflows
## image-reuse.yaml
- The resuable workflow can be used to publish or build images with multiple container registries(Quay,GHCR, dockerhub), and then sign them with cosign when an image is published.
- A GO version `must` be specified e.g. 1.20
- The image name for each registry *must* contain the tag. Note: multiple tags are allowed for each registry using a CSV type.
- Multiple platforms can be specified e.g. linux/amd64,linux/arm64
- Images are not published by default. A boolean value must be set to `true` to push images.
- An optional target can be specified.
| Inputs | Description | Type | Required | Defaults |
|-------------------|-------------------------------------|-------------|----------|-----------------|
| go-version | Version of Go to be used | string | true | none |
| quay_image_name | Full image name and tag | CSV, string | false | none |
| ghcr_image_name | Full image name and tag | CSV, string | false | none |
| docker_image_name | Full image name and tag | CSV, string | false | none |
| platforms | Platforms to build (linux/amd64) | CSV, string | false | linux/amd64 |
| push | Whether to push image/s to registry | boolean | false | false |
| target | Target build stage | string | false | none |
| Outputs | Description | Type |
|-------------|------------------------------------------|-------|
|image-digest | Image digest of image container created | string|

View File

@@ -9,11 +9,10 @@ on:
pull_request:
branches:
- 'master'
- 'release-*'
env:
# Golang version to use across CI steps
GOLANG_VERSION: '1.20'
GOLANG_VERSION: '1.18'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -28,9 +27,9 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- name: Setup Golang
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Download all Go modules
@@ -46,13 +45,13 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- name: Setup Golang
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Restore go build cache
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
@@ -70,15 +69,15 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- name: Setup Golang
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Run golangci-lint
uses: golangci/golangci-lint-action@0ad9a0988b3973e851ab0a07adf248ec2e100376 # v3.3.1
with:
version: v1.51.0
version: v1.46.2
args: --timeout 10m --exclude SA5011 --verbose
test-go:
@@ -93,11 +92,11 @@ jobs:
- name: Create checkout directory
run: mkdir -p ~/go/src/github.com/argoproj
- name: Checkout code
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- name: Create symlink in GOPATH
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
- name: Setup Golang
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Install required packages
@@ -117,7 +116,7 @@ jobs:
run: |
echo "/usr/local/bin" >> $GITHUB_PATH
- name: Restore go build cache
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
@@ -160,11 +159,11 @@ jobs:
- name: Create checkout directory
run: mkdir -p ~/go/src/github.com/argoproj
- name: Checkout code
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- name: Create symlink in GOPATH
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
- name: Setup Golang
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Install required packages
@@ -184,7 +183,7 @@ jobs:
run: |
echo "/usr/local/bin" >> $GITHUB_PATH
- name: Restore go build cache
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
@@ -215,9 +214,9 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- name: Setup Golang
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Create symlink in GOPATH
@@ -263,14 +262,14 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- name: Setup NodeJS
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
node-version: '18.15.0'
node-version: '12.18.4'
- name: Restore node dependency cache
id: cache-dependencies
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6
with:
path: ui/node_modules
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
@@ -300,12 +299,12 @@ jobs:
sonar_secret: ${{ secrets.SONAR_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
with:
fetch-depth: 0
- name: Restore node dependency cache
id: cache-dependencies
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6
with:
path: ui/node_modules
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
@@ -361,7 +360,7 @@ jobs:
runs-on: ubuntu-22.04
strategy:
matrix:
k3s-version: [v1.26.0, v1.25.4, v1.24.3, v1.23.3]
k3s-version: [v1.24.3, v1.23.3, v1.22.6]
needs:
- build-go
env:
@@ -379,9 +378,9 @@ jobs:
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- name: Setup Golang
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: GH actions workaround - Kill XSP4 process
@@ -399,7 +398,7 @@ jobs:
sudo chown runner $HOME/.kube/config
kubectl version
- name: Restore go build cache
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
@@ -425,9 +424,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.37.0
docker pull ghcr.io/dexidp/dex:v2.35.3
docker pull argoproj/argo-cd-ci-builder:v1.0.0
docker pull redis:7.0.14-alpine
docker pull redis:7.0.8-alpine
- name: Create target directory for binaries in the build-process
run: |
mkdir -p dist

View File

@@ -5,7 +5,6 @@ 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'
@@ -30,7 +29,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL

View File

@@ -1,151 +0,0 @@
name: Publish and Sign Container Image
on:
workflow_call:
inputs:
go-version:
required: true
type: string
quay_image_name:
required: false
type: string
ghcr_image_name:
required: false
type: string
docker_image_name:
required: false
type: string
platforms:
required: true
type: string
default: linux/amd64
push:
required: true
type: boolean
default: false
target:
required: false
type: string
secrets:
quay_username:
required: false
quay_password:
required: false
ghcr_username:
required: false
ghcr_password:
required: false
docker_username:
required: false
docker_password:
required: false
outputs:
image-digest:
description: "sha256 digest of container image"
value: ${{ jobs.publish.outputs.image-digest }}
permissions: {}
jobs:
publish:
permissions:
contents: read
packages: write # Used to push images to `ghcr.io` if used.
id-token: write # Needed to create an OIDC token for keyless signing
runs-on: ubuntu-22.04
outputs:
image-digest: ${{ steps.image.outputs.digest }}
steps:
- name: Checkout code
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.3.0
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
if: ${{ github.ref_type == 'tag'}}
- name: Checkout code
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
if: ${{ github.ref_type != 'tag'}}
- name: Setup Golang
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
with:
go-version: ${{ inputs.go-version }}
- name: Install cosign
uses: sigstore/cosign-installer@e1523de7571e31dbe865fd2e80c5c7c23ae71eb4 # v3.4.0
- uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.1.0
- uses: docker/setup-buildx-action@4b4e9c3e2d4531116a6f8ba8e71fc6e2cb6e6c8c # v2.5.0
- name: Setup tags for container image as a CSV type
run: |
IMAGE_TAGS=$(for str in \
${{ inputs.quay_image_name }} \
${{ inputs.ghcr_image_name }} \
${{ inputs.docker_image_name}}; do
echo -n "${str}",;done | sed 's/,$//')
echo $IMAGE_TAGS
echo "TAGS=$IMAGE_TAGS" >> $GITHUB_ENV
- name: Setup image namespace for signing, strip off the tag
run: |
TAGS=$(for tag in \
${{ inputs.quay_image_name }} \
${{ inputs.ghcr_image_name }} \
${{ inputs.docker_image_name}}; do
echo -n "${tag}" | awk -F ":" '{print $1}' -;done)
echo $TAGS
echo 'SIGNING_TAGS<<EOF' >> $GITHUB_ENV
echo $TAGS >> $GITHUB_ENV
echo 'EOF' >> $GITHUB_ENV
- name: Login to Quay.io
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2.1.0
with:
registry: quay.io
username: ${{ secrets.quay_username }}
password: ${{ secrets.quay_password }}
if: ${{ inputs.quay_image_name && inputs.push }}
- name: Login to GitHub Container Registry
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2.1.0
with:
registry: ghcr.io
username: ${{ secrets.ghcr_username }}
password: ${{ secrets.ghcr_password }}
if: ${{ inputs.ghcr_image_name && inputs.push }}
- name: Login to dockerhub Container Registry
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2.1.0
with:
username: ${{ secrets.docker_username }}
password: ${{ secrets.docker_password }}
if: ${{ inputs.docker_image_name && inputs.push }}
- name: Build and push container image
id: image
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 #v4.0.0
with:
context: .
platforms: ${{ inputs.platforms }}
push: ${{ inputs.push }}
tags: ${{ env.TAGS }}
target: ${{ inputs.target }}
provenance: false
sbom: false
- name: Sign container images
run: |
for signing_tag in $SIGNING_TAGS; do
cosign sign \
-a "repo=${{ github.repository }}" \
-a "workflow=${{ github.workflow }}" \
-a "sha=${{ github.sha }}" \
-y \
"$signing_tag"@${{ steps.image.outputs.digest }}
done
if: ${{ inputs.push }}

View File

@@ -9,108 +9,97 @@ on:
- master
types: [ labeled, unlabeled, opened, synchronize, reopened ]
env:
GOLANG_VERSION: '1.18'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions: {}
permissions:
contents: read
jobs:
set-vars:
publish:
permissions:
contents: read
contents: write # for git to push upgrade commit if not already deployed
packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags
if: github.repository == 'argoproj/argo-cd'
runs-on: ubuntu-22.04
outputs:
image-tag: ${{ steps.image.outputs.tag}}
platforms: ${{ steps.platforms.outputs.platforms }}
env:
GOPATH: /home/runner/work/argo-cd/argo-cd
steps:
- uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
- uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
with:
path: src/github.com/argoproj/argo-cd
- name: Set image tag for ghcr
run: echo "tag=$(cat ./VERSION)-${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
# get image tag
- run: echo "tag=$(cat ./VERSION)-${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
working-directory: ./src/github.com/argoproj/argo-cd
id: image
- name: Determine image platforms to use
id: platforms
run: |
# login
- run: |
docker login ghcr.io --username $USERNAME --password-stdin <<< "$PASSWORD"
docker login quay.io --username "$DOCKER_USERNAME" --password-stdin <<< "$DOCKER_TOKEN"
if: github.event_name == 'push'
env:
USERNAME: ${{ github.actor }}
PASSWORD: ${{ secrets.GITHUB_TOKEN }}
DOCKER_USERNAME: ${{ secrets.RELEASE_QUAY_USERNAME }}
DOCKER_TOKEN: ${{ secrets.RELEASE_QUAY_TOKEN }}
# build
- uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.1.0
- uses: docker/setup-buildx-action@f03ac48505955848960e80bbb68046aa35c7b9e7 # v2.4.1
- run: |
IMAGE_PLATFORMS=linux/amd64
if [[ "${{ github.event_name }}" == "push" || "${{ contains(github.event.pull_request.labels.*.name, 'test-multi-image') }}" == "true" ]]
if [[ "${{ github.event_name }}" == "push" || "${{ contains(github.event.pull_request.labels.*.name, 'test-arm-image') }}" == "true" ]]
then
IMAGE_PLATFORMS=linux/amd64,linux/arm64,linux/s390x,linux/ppc64le
fi
echo "Building image for platforms: $IMAGE_PLATFORMS"
echo "platforms=$IMAGE_PLATFORMS" >> $GITHUB_OUTPUT
docker buildx build --platform $IMAGE_PLATFORMS --sbom=false --provenance=false --push="${{ github.event_name == 'push' }}" \
-t ghcr.io/argoproj/argo-cd/argocd:${{ steps.image.outputs.tag }} \
-t quay.io/argoproj/argocd:latest .
working-directory: ./src/github.com/argoproj/argo-cd
build-only:
needs: [set-vars]
permissions:
contents: read
packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags
id-token: write # for creating OIDC tokens for signing.
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name != 'push' }}
uses: ./.github/workflows/image-reuse.yaml
with:
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
go-version: 1.20
platforms: ${{ needs.set-vars.outputs.platforms }}
push: false
# sign container images
- name: Install cosign
uses: sigstore/cosign-installer@c3667d99424e7e6047999fb6246c0da843953c65 # v3.0.1
with:
cosign-release: 'v1.13.1'
build-and-publish:
needs: [set-vars]
permissions:
contents: read
packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags
id-token: write # for creating OIDC tokens for signing.
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
uses: ./.github/workflows/image-reuse.yaml
with:
quay_image_name: quay.io/argoproj/argocd:latest
ghcr_image_name: ghcr.io/argoproj/argo-cd/argocd:${{ needs.set-vars.outputs.image-tag }}
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
go-version: 1.20
platforms: ${{ needs.set-vars.outputs.platforms }}
push: true
secrets:
quay_username: ${{ secrets.RELEASE_QUAY_USERNAME }}
quay_password: ${{ secrets.RELEASE_QUAY_TOKEN }}
ghcr_username: ${{ github.actor }}
ghcr_password: ${{ secrets.GITHUB_TOKEN }}
- name: Install crane to get digest of image
uses: imjasonh/setup-crane@00c9e93efa4e1138c9a7a5c594acd6c75a2fbf0c
build-and-publish-provenance:
needs: [build-and-publish]
permissions:
actions: read # for detecting the Github Actions environment.
id-token: write # for creating OIDC tokens for signing.
packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.10.0
with:
image: quay.io/argoproj/argocd
digest: ${{ needs.build-and-publish.outputs.image-digest }}
secrets:
registry-username: ${{ secrets.RELEASE_QUAY_USERNAME }}
registry-password: ${{ secrets.RELEASE_QUAY_TOKEN }}
- name: Get digest of image
run: |
echo "IMAGE_DIGEST=$(crane digest quay.io/argoproj/argocd:latest)" >> $GITHUB_ENV
Deploy:
needs:
- build-and-publish
- set-vars
permissions:
contents: write # for git to push upgrade commit if not already deployed
packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- name: Sign Argo CD latest image
run: |
cosign sign --key env://COSIGN_PRIVATE_KEY quay.io/argoproj/argocd@${{ env.IMAGE_DIGEST }}
# Displays the public key to share.
cosign public-key --key env://COSIGN_PRIVATE_KEY
env:
COSIGN_PRIVATE_KEY: ${{secrets.COSIGN_PRIVATE_KEY}}
COSIGN_PASSWORD: ${{secrets.COSIGN_PASSWORD}}
if: ${{ github.event_name == 'push' }}
# deploy
- run: git clone "https://$TOKEN@github.com/argoproj/argoproj-deployments"
if: github.event_name == 'push'
env:
TOKEN: ${{ secrets.TOKEN }}
- run: |
docker run -u $(id -u):$(id -g) -v $(pwd):/src -w /src --rm -t ghcr.io/argoproj/argo-cd/argocd:${{ needs.set-vars.outputs.image-tag }} kustomize edit set image quay.io/argoproj/argocd=ghcr.io/argoproj/argo-cd/argocd:${{ needs.set-vars.outputs.image-tag }}
docker run -u $(id -u):$(id -g) -v $(pwd):/src -w /src --rm -t ghcr.io/argoproj/argo-cd/argocd:${{ steps.image.outputs.tag }} kustomize edit set image quay.io/argoproj/argocd=ghcr.io/argoproj/argo-cd/argocd:${{ steps.image.outputs.tag }}
git config --global user.email 'ci@argoproj.com'
git config --global user.name 'CI'
git diff --exit-code && echo 'Already deployed' || (git commit -am 'Upgrade argocd to ${{ needs.set-vars.outputs.image-tag }}' && git push)
git diff --exit-code && echo 'Already deployed' || (git commit -am 'Upgrade argocd to ${{ steps.image.outputs.tag }}' && git push)
if: github.event_name == 'push'
working-directory: argoproj-deployments/argocd
# TODO: clean up old images once github supports it: https://github.community/t5/How-to-use-Git-and-GitHub/Deleting-images-from-GitHub-Package-Registry/m-p/41202/thread-id/9811

View File

@@ -1,70 +0,0 @@
name: Init ArgoCD Release
on:
workflow_dispatch:
inputs:
TARGET_BRANCH:
description: 'TARGET_BRANCH to checkout (e.g. release-2.5)'
required: true
type: string
TARGET_VERSION:
description: 'TARGET_VERSION to build manifests (e.g. 2.5.0-rc1) Note: the `v` prefix is not used'
required: true
type: string
permissions: {}
jobs:
prepare-release:
permissions:
contents: write # for peter-evans/create-pull-request to create branch
pull-requests: write # for peter-evans/create-pull-request to create a PR
name: Automatically generate version and manifests on ${{ inputs.TARGET_BRANCH }}
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ inputs.TARGET_BRANCH }}
- name: Check if TARGET_VERSION is well formed.
run: |
set -xue
# Target version must not contain 'v' prefix
if echo "${{ inputs.TARGET_VERSION }}" | grep -e '^v'; then
echo "::error::Target version '${{ inputs.TARGET_VERSION }}' should not begin with a 'v' prefix, refusing to continue." >&2
exit 1
fi
- name: Create VERSION information
run: |
set -ue
echo "Bumping version from $(cat VERSION) to ${{ inputs.TARGET_VERSION }}"
echo "${{ inputs.TARGET_VERSION }}" > VERSION
# We install kustomize in the dist directory
- name: Add dist to PATH
run: |
echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH
- name: Generate new set of manifests
run: |
set -ue
make install-codegen-tools-local
make manifests-local VERSION=${{ inputs.TARGET_VERSION }}
git diff
- name: Create pull request
uses: peter-evans/create-pull-request@38e0b6e68b4c852a5500a94740f0e535e0d7ba54 # v4.2.4
with:
commit-message: "Bump version to ${{ inputs.TARGET_VERSION }}"
title: "Bump version to ${{ inputs.TARGET_VERSION }} on ${{ inputs.TARGET_BRANCH }} branch"
body: Updating VERSION and manifests to ${{ inputs.TARGET_VERSION }}
branch: update-version
branch-suffix: random
signoff: true
labels: release

View File

@@ -1,41 +0,0 @@
name: "Lint PR"
on:
pull_request_target:
types:
- opened
- edited
- synchronize
# IMPORTANT: No checkout actions, scripts, or builds should be added to this workflow. Permissions should always be used
# with extreme caution.
permissions:
contents: read
# PR updates can happen in quick succession leading to this
# workflow being trigger a number of times. This limits it
# to one run per PR.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
main:
permissions:
pull-requests: read # for amannn/action-semantic-pull-request to analyze PRs
statuses: write # for amannn/action-semantic-pull-request to mark status of analyzed PR
name: Validate PR title
runs-on: ubuntu-latest
steps:
# IMPORTANT: Carefully review changes when updating this action. Using the pull_request_target event requires caution.
- uses: amannn/action-semantic-pull-request@b6bca70dcd3e56e896605356ce09b76f7e1e0d39 # v5.1.0
with:
types: |
feat
fix
docs
test
ci
chore
[Bot] docs
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,157 +1,270 @@
name: Publish ArgoCD Release
name: Create ArgoCD release
on:
push:
tags:
- 'v*'
- '!v2.4*'
- '!v2.5*'
- '!v2.6*'
permissions: {}
- "release-v*"
- "!release-v1.5*"
- "!release-v1.4*"
- "!release-v1.3*"
- "!release-v1.2*"
- "!release-v1.1*"
- "!release-v1.0*"
- "!release-v0*"
env:
GOLANG_VERSION: '1.20' # Note: go-version must also be set in job argocd-image.with.go-version
GOLANG_VERSION: '1.18'
permissions:
contents: read
jobs:
argocd-image:
prepare-release:
permissions:
contents: read
id-token: write # for creating OIDC tokens for signing.
packages: write # used to push images to `ghcr.io` if used.
if: github.repository == 'argoproj/argo-cd'
uses: ./.github/workflows/image-reuse.yaml
with:
quay_image_name: quay.io/argoproj/argocd:${{ github.ref_name }}
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
go-version: 1.20
platforms: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le
push: true
secrets:
quay_username: ${{ secrets.RELEASE_QUAY_USERNAME }}
quay_password: ${{ secrets.RELEASE_QUAY_TOKEN }}
argocd-image-provenance:
needs: [argocd-image]
permissions:
actions: read # for detecting the Github Actions environment.
id-token: write # for creating OIDC tokens for signing.
packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
if: github.repository == 'argoproj/argo-cd'
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.10.0
with:
image: quay.io/argoproj/argocd
digest: ${{ needs.argocd-image.outputs.image-digest }}
secrets:
registry-username: ${{ secrets.RELEASE_QUAY_USERNAME }}
registry-password: ${{ secrets.RELEASE_QUAY_TOKEN }}
goreleaser:
needs:
- argocd-image
- argocd-image-provenance
permissions:
contents: write # used for uploading assets
contents: write # To push changes to release branch
name: Perform automatic release on trigger ${{ github.ref }}
if: github.repository == 'argoproj/argo-cd'
runs-on: ubuntu-22.04
outputs:
hashes: ${{ steps.hash.outputs.hashes }}
env:
# The name of the tag as supplied by the GitHub event
SOURCE_TAG: ${{ github.ref }}
# The image namespace where Docker image will be published to
IMAGE_NAMESPACE: quay.io/argoproj
# Whether to create & push image and release assets
DRY_RUN: false
# Whether a draft release should be created, instead of public one
DRAFT_RELEASE: false
# Whether to update homebrew with this release as well
# Set RELEASE_HOMEBREW_TOKEN secret in repository for this to work - needs
# access to public repositories
UPDATE_HOMEBREW: false
# Name of the GitHub user for Git config
GIT_USERNAME: argo-bot
# E-Mail of the GitHub user for Git config
GIT_EMAIL: argoproj@gmail.com
steps:
- name: Checkout code
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Fetch all tags
run: git fetch --force --tags
- name: Set GORELEASER_PREVIOUS_TAG # Workaround, GoReleaser uses 'git-describe' to determine a previous tag. Our tags are created in realease branches.
- name: Check if the published tag is well formed and setup vars
run: |
set -xue
if echo ${{ github.ref_name }} | grep -E -- '-rc1+$';then
echo "GORELEASER_PREVIOUS_TAG=$(git -c 'versionsort.suffix=-rc' tag --list --sort=version:refname | tail -n 2 | head -n 1)" >> $GITHUB_ENV
else
echo "This is not the first release on the branch, Using GoReleaser defaults"
# Target version must match major.minor.patch and optional -rcX suffix
# where X must be a number.
TARGET_VERSION=${SOURCE_TAG#*release-v}
if ! echo "${TARGET_VERSION}" | egrep '^[0-9]+\.[0-9]+\.[0-9]+(-rc[0-9]+)*$'; then
echo "::error::Target version '${TARGET_VERSION}' is malformed, refusing to continue." >&2
exit 1
fi
- name: Setup Golang
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
with:
go-version: ${{ env.GOLANG_VERSION }}
# Target branch is the release branch we're going to operate on
# Its name is 'release-<major>.<minor>'
TARGET_BRANCH="release-${TARGET_VERSION%\.[0-9]*}"
- name: Set environment variables for ldflags
id: set_ldflag
run: |
echo "KUBECTL_VERSION=$(go list -m k8s.io/client-go | head -n 1 | rev | cut -d' ' -f1 | rev)" >> $GITHUB_ENV
echo "GIT_TREE_STATE=$(if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)" >> $GITHUB_ENV
# The release tag is the source tag, minus the release- prefix
RELEASE_TAG="${SOURCE_TAG#*release-}"
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@f82d6c1c344bcacabba2c841718984797f664a6b # v4.2.0
id: run-goreleaser
with:
version: latest
args: release --clean --timeout 55m
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
KUBECTL_VERSION: ${{ env.KUBECTL_VERSION }}
GIT_TREE_STATE: ${{ env.GIT_TREE_STATE }}
- name: Generate subject for provenance
id: hash
env:
ARTIFACTS: "${{ steps.run-goreleaser.outputs.artifacts }}"
run: |
set -euo pipefail
hashes=$(echo $ARTIFACTS | jq --raw-output '.[] | {name, "digest": (.extra.Digest // .extra.Checksum)} | select(.digest) | {digest} + {name} | join(" ") | sub("^sha256:";"")' | base64 -w0)
if test "$hashes" = ""; then # goreleaser < v1.13.0
checksum_file=$(echo "$ARTIFACTS" | jq -r '.[] | select (.type=="Checksum") | .path')
hashes=$(cat $checksum_file | base64 -w0)
# Whether this is a pre-release (indicated by -rc suffix)
PRE_RELEASE=false
if echo "${RELEASE_TAG}" | egrep -- '-rc[0-9]+$'; then
PRE_RELEASE=true
fi
echo "hashes=$hashes" >> $GITHUB_OUTPUT
goreleaser-provenance:
needs: [goreleaser]
permissions:
actions: read # for detecting the Github Actions environment
id-token: write # Needed for provenance signing and ID
contents: write # Needed for release uploads
if: github.repository == 'argoproj/argo-cd'
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.10.0
with:
base64-subjects: "${{ needs.goreleaser.outputs.hashes }}"
provenance-name: "argocd-cli.intoto.jsonl"
upload-assets: true
# We must not have a release trigger within the same release branch,
# because that means a release for this branch is already running.
if git tag -l | grep "release-v${TARGET_VERSION%\.[0-9]*}" | grep -v "release-v${TARGET_VERSION}"; then
echo "::error::Another release for branch ${TARGET_BRANCH} is currently in progress."
exit 1
fi
generate-sbom:
name: Create Sbom and sign assets
needs:
- argocd-image
- goreleaser
permissions:
contents: write # Needed for release uploads
id-token: write # Needed for signing Sbom
if: github.repository == 'argoproj/argo-cd'
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
# Ensure that release do not yet exist
if git rev-parse ${RELEASE_TAG}; then
echo "::error::Release tag ${RELEASE_TAG} already exists in repository. Refusing to continue."
exit 1
fi
# Make the variables available in follow-up steps
echo "TARGET_VERSION=${TARGET_VERSION}" >> $GITHUB_ENV
echo "TARGET_BRANCH=${TARGET_BRANCH}" >> $GITHUB_ENV
echo "RELEASE_TAG=${RELEASE_TAG}" >> $GITHUB_ENV
echo "PRE_RELEASE=${PRE_RELEASE}" >> $GITHUB_ENV
- name: Check if our release tag has a correct annotation
run: |
set -ue
# Fetch all tag information as well
git fetch --prune --tags --force
echo "=========== BEGIN COMMIT MESSAGE ============="
git show ${SOURCE_TAG}
echo "============ END COMMIT MESSAGE =============="
# Quite dirty hack to get the release notes from the annotated tag
# into a temporary file.
RELEASE_NOTES=$(mktemp -p /tmp release-notes.XXXXXX)
prefix=true
begin=false
git show ${SOURCE_TAG} | while read line; do
# Whatever is in commit history for the tag, we only want that
# annotation from our tag. We discard everything else.
if test "$begin" = "false"; then
if echo "$line" | grep -q "tag ${SOURCE_TAG#refs/tags/}"; then begin="true"; fi
continue
fi
if test "$prefix" = "true"; then
if test -z "$line"; then prefix=false; fi
else
if echo "$line" | egrep -q '^commit [0-9a-f]+'; then
break
fi
echo "$line" >> ${RELEASE_NOTES}
fi
done
# For debug purposes
echo "============BEGIN RELEASE NOTES================="
cat ${RELEASE_NOTES}
echo "=============END RELEASE NOTES=================="
# Too short release notes are suspicious. We need at least 100 bytes.
relNoteLen=$(stat -c '%s' $RELEASE_NOTES)
if test $relNoteLen -lt 100; then
echo "::error::No release notes provided in tag annotation (or tag is not annotated)"
exit 1
fi
# Check for magic string '## Quick Start' in head of release notes
if ! head -2 ${RELEASE_NOTES} | grep -iq '## Quick Start'; then
echo "::error::Release notes seem invalid, quick start section not found."
exit 1
fi
# We store path to temporary release notes file for later reading, we
# need it when creating release.
echo "RELEASE_NOTES=${RELEASE_NOTES}" >> $GITHUB_ENV
- name: Setup Golang
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: Setup Git author information
run: |
set -ue
git config --global user.email "${GIT_EMAIL}"
git config --global user.name "${GIT_USERNAME}"
- name: Checkout corresponding release branch
run: |
set -ue
echo "Switching to release branch '${TARGET_BRANCH}'"
if ! git checkout ${TARGET_BRANCH}; then
echo "::error::Checking out release branch '${TARGET_BRANCH}' for target version '${TARGET_VERSION}' (tagged '${RELEASE_TAG}') failed. Does it exist in repo?"
exit 1
fi
- name: Create VERSION information
run: |
set -ue
echo "Bumping version from $(cat VERSION) to ${TARGET_VERSION}"
echo "${TARGET_VERSION}" > VERSION
git commit -m "Bump version to ${TARGET_VERSION}" VERSION
- name: Generate new set of manifests
run: |
set -ue
make install-codegen-tools-local
# We install kustomize in the dist directory
echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH
make manifests-local VERSION=${TARGET_VERSION}
git diff
git commit manifests/ -m "Bump version to ${TARGET_VERSION}"
- name: Create the release tag
run: |
set -ue
echo "Creating release ${RELEASE_TAG}"
git tag ${RELEASE_TAG}
- name: Login to docker repositories
env:
DOCKER_USERNAME: ${{ secrets.RELEASE_DOCKERHUB_USERNAME }}
DOCKER_TOKEN: ${{ secrets.RELEASE_DOCKERHUB_TOKEN }}
QUAY_USERNAME: ${{ secrets.RELEASE_QUAY_USERNAME }}
QUAY_TOKEN: ${{ secrets.RELEASE_QUAY_TOKEN }}
run: |
set -ue
docker login quay.io --username "${QUAY_USERNAME}" --password-stdin <<< "${QUAY_TOKEN}"
# Remove the following when Docker Hub is gone
docker login --username "${DOCKER_USERNAME}" --password-stdin <<< "${DOCKER_TOKEN}"
if: ${{ env.DRY_RUN != 'true' }}
- uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.1.0
- uses: docker/setup-buildx-action@f03ac48505955848960e80bbb68046aa35c7b9e7 # v2.4.1
- name: Build and push Docker image for release
run: |
set -ue
git clean -fd
mkdir -p dist/
docker buildx build --platform linux/amd64,linux/arm64,linux/s390x,linux/ppc64le --sbom=false --provenance=false --push -t ${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION} -t argoproj/argocd:v${TARGET_VERSION} .
make release-cli
make checksums
chmod +x ./dist/argocd-linux-amd64
./dist/argocd-linux-amd64 version --client
if: ${{ env.DRY_RUN != 'true' }}
- name: Install cosign
uses: sigstore/cosign-installer@1fc5bd396d372bee37d608f955b336615edf79c8 # v3.2.0
uses: sigstore/cosign-installer@c3667d99424e7e6047999fb6246c0da843953c65 # v3.0.1
with:
cosign-release: 'v2.2.1'
cosign-release: 'v1.13.1'
- name: Install crane to get digest of image
uses: imjasonh/setup-crane@00c9e93efa4e1138c9a7a5c594acd6c75a2fbf0c
- name: Get digest of image
run: |
echo "IMAGE_DIGEST=$(crane digest quay.io/argoproj/argocd:v${TARGET_VERSION})" >> $GITHUB_ENV
- name: Sign Argo CD container images and assets
run: |
cosign sign --key env://COSIGN_PRIVATE_KEY ${IMAGE_NAMESPACE}/argocd@${{ env.IMAGE_DIGEST }}
cosign sign-blob --key env://COSIGN_PRIVATE_KEY ./dist/argocd-${TARGET_VERSION}-checksums.txt > ./dist/argocd-${TARGET_VERSION}-checksums.sig
# Retrieves the public key to release as an asset
cosign public-key --key env://COSIGN_PRIVATE_KEY > ./dist/argocd-cosign.pub
env:
COSIGN_PRIVATE_KEY: ${{secrets.COSIGN_PRIVATE_KEY}}
COSIGN_PASSWORD: ${{secrets.COSIGN_PASSWORD}}
if: ${{ env.DRY_RUN != 'true' }}
- name: Read release notes file
id: release-notes
uses: juliangruber/read-file-action@02bbba9876a8f870efd4ad64e3b9088d3fb94d4b # v1.1.6
with:
path: ${{ env.RELEASE_NOTES }}
- name: Push changes to release branch
run: |
set -ue
git push origin ${TARGET_BRANCH}
git push origin ${RELEASE_TAG}
- name: Dry run GitHub release
uses: actions/create-release@0cb9c9b65d5d1901c1f53e5e66eaf4afd303e70e # v1.1.4
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
id: create_release
with:
tag_name: ${{ env.RELEASE_TAG }}
release_name: ${{ env.RELEASE_TAG }}
draft: ${{ env.DRAFT_RELEASE }}
prerelease: ${{ env.PRE_RELEASE }}
body: ${{ steps.release-notes.outputs.content }}
if: ${{ env.DRY_RUN == 'true' }}
- name: Generate SBOM (spdx)
id: spdx-builder
@@ -164,7 +277,7 @@ jobs:
# managers (gomod, yarn, npm).
PROJECT_FOLDERS: ".,./ui"
# full qualified name of the docker image to be inspected
DOCKER_IMAGE: quay.io/argoproj/argocd:${{ github.ref_name }}
DOCKER_IMAGE: ${{env.IMAGE_NAMESPACE}}/argocd:v${{env.TARGET_VERSION}}
run: |
yarn install --cwd ./ui
go install github.com/spdx/spdx-sbom-generator/cmd/generator@$SPDX_GEN_VERSION
@@ -182,115 +295,43 @@ jobs:
fi
cd /tmp && tar -zcf sbom.tar.gz *.spdx
if: ${{ env.DRY_RUN != 'true' }}
- name: Sign SBOM
- name: Sign sbom
run: |
cosign sign-blob \
--output-certificate=/tmp/sbom.tar.gz.pem \
--output-signature=/tmp/sbom.tar.gz.sig \
-y \
/tmp/sbom.tar.gz
cosign sign-blob --key env://COSIGN_PRIVATE_KEY /tmp/sbom.tar.gz > /tmp/sbom.tar.gz.sig
env:
COSIGN_PRIVATE_KEY: ${{secrets.COSIGN_PRIVATE_KEY}}
COSIGN_PASSWORD: ${{secrets.COSIGN_PASSWORD}}
if: ${{ env.DRY_RUN != 'true' }}
- name: Upload SBOM and signature assets
- name: Create GitHub release
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
name: ${{ env.RELEASE_TAG }}
tag_name: ${{ env.RELEASE_TAG }}
draft: ${{ env.DRAFT_RELEASE }}
prerelease: ${{ env.PRE_RELEASE }}
body: ${{ steps.release-notes.outputs.content }} # Pre-pended to the generated notes
files: |
dist/argocd-*
/tmp/sbom.tar.gz
sbom-provenance:
needs: [generate-sbom]
permissions:
actions: read # for detecting the Github Actions environment
id-token: write # Needed for provenance signing and ID
contents: write # Needed for release uploads
if: github.repository == 'argoproj/argo-cd'
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.10.0
with:
base64-subjects: "${{ needs.generate-sbom.outputs.hashes }}"
provenance-name: "argocd-sbom.intoto.jsonl"
upload-assets: true
/tmp/sbom.tar.gz.sig
if: ${{ env.DRY_RUN != 'true' }}
post-release:
needs:
- argocd-image
- goreleaser
- generate-sbom
permissions:
contents: write # Needed to push commit to update stable tag
pull-requests: write # Needed to create PR for VERSION update.
if: github.repository == 'argoproj/argo-cd'
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
- name: Update homebrew formula
env:
HOMEBREW_TOKEN: ${{ secrets.RELEASE_HOMEBREW_TOKEN }}
uses: dawidd6/action-homebrew-bump-formula@02e79d9da43d79efa846d73695b6052cbbdbf48a # v3.8.3
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
token: ${{env.HOMEBREW_TOKEN}}
formula: argocd
if: ${{ env.HOMEBREW_TOKEN != '' && env.UPDATE_HOMEBREW == 'true' && env.PRE_RELEASE != 'true' }}
- name: Setup Git author information
- name: Delete original request tag from repository
run: |
set -ue
git config --global user.email 'ci@argoproj.com'
git config --global user.name 'CI'
- name: Check if tag is the latest version and not a pre-release
run: |
set -xue
# Fetch all tag information
git fetch --prune --tags --force
LATEST_TAG=$(git -c 'versionsort.suffix=-rc' tag --list --sort=version:refname | tail -n1)
PRE_RELEASE=false
# Check if latest tag is a pre-release
if echo $LATEST_TAG | grep -E -- '-rc[0-9]+$';then
PRE_RELEASE=true
fi
# Ensure latest tag matches github.ref_name & not a pre-release
if [[ $LATEST_TAG == ${{ github.ref_name }} ]] && [[ $PRE_RELEASE != 'true' ]];then
echo "TAG_STABLE=true" >> $GITHUB_ENV
else
echo "TAG_STABLE=false" >> $GITHUB_ENV
fi
- name: Update stable tag to latest version
run: |
git tag -f stable ${{ github.ref_name }}
git push -f origin stable
if: ${{ env.TAG_STABLE == 'true' }}
- name: Check to see if VERSION should be updated on master branch
run: |
set -xue
SOURCE_TAG=${{ github.ref_name }}
VERSION_REF="${SOURCE_TAG#*v}"
if echo "$VERSION_REF" | grep -E -- '^[0-9]+\.[0-9]+\.0$';then
VERSION=$(awk 'BEGIN {FS=OFS="."} {$2++; print}' <<< "${VERSION_REF}")
echo "Updating VERSION to: $VERSION"
echo "UPDATE_VERSION=true" >> $GITHUB_ENV
echo "NEW_VERSION=$VERSION" >> $GITHUB_ENV
else
echo "Not updating VERSION"
echo "UPDATE_VERSION=false" >> $GITHUB_ENV
fi
- name: Update VERSION on master branch
run: |
echo ${{ env.NEW_VERSION }} > VERSION
if: ${{ env.UPDATE_VERSION == 'true' }}
- name: Create PR to update VERSION on master branch
uses: peter-evans/create-pull-request@38e0b6e68b4c852a5500a94740f0e535e0d7ba54 # v4.2.4
with:
commit-message: Bump version in master
title: "chore: Bump version in master"
body: All images built from master should indicate which version we are on track for.
signoff: true
branch: update-version
branch-suffix: random
base: master
if: ${{ env.UPDATE_VERSION == 'true' }}
git push --delete origin ${SOURCE_TAG}
if: ${{ always() }}

View File

@@ -1,67 +0,0 @@
name: Scorecards supply-chain security
on:
# Only the default branch is supported.
branch_protection_rule:
schedule:
- cron: "39 9 * * 2"
push:
branches: ["master"]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecards analysis
runs-on: ubuntu-22.04
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Used to receive a badge. (Upcoming feature)
id-token: write
# Needs for private repositories.
contents: read
actions: read
if: github.repository == 'argoproj/argo-cd'
steps:
- name: "Checkout code"
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@e38b1902ae4f44df626f11ba0734b14fb91f8f86 # v2.1.2
with:
results_file: results.sarif
results_format: sarif
# (Optional) Read-only PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecards on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
# repo_token: ${{ secrets.SCORECARD_READ_TOKEN }}
# Publish the results for public repositories to enable scorecard badges. For more details, see
# https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories, `publish_results` will automatically be set to `false`, regardless
# of the value entered here.
publish_results: true
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@3ebbd71c74ef574dbc558c82f70e52732c8b44fe # v2.2.1
with:
sarif_file: results.sarif

View File

@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Build reports
@@ -31,6 +31,6 @@ jobs:
git config --global user.email 'ci@argoproj.com'
git config --global user.name 'CI'
git add docs/snyk
git commit -m "[Bot] docs: Update Snyk reports" --signoff
git commit -m "[Bot] Update Snyk reports" --signoff
git push --set-upstream origin "$pr_branch"
gh pr create -B master -H "$pr_branch" --title '[Bot] docs: Update Snyk report' --body ''

2
.gitpod.Dockerfile vendored
View File

@@ -1,4 +1,4 @@
FROM gitpod/workspace-full@sha256:d5787229cd062aceae91109f1690013d3f25062916492fb7f444d13de3186178
FROM gitpod/workspace-full
USER root

View File

@@ -1,120 +0,0 @@
project_name: argocd
before:
hooks:
- go mod download
- make build-ui
builds:
- id: argocd-cli
main: ./cmd
binary: argocd-{{ .Os}}-{{ .Arch}}
env:
- CGO_ENABLED=0
flags:
- -v
ldflags:
- -X github.com/argoproj/argo-cd/v2/common.version={{ .Version }}
- -X github.com/argoproj/argo-cd/v2/common.buildDate={{ .Date }}
- -X github.com/argoproj/argo-cd/v2/common.gitCommit={{ .FullCommit }}
- -X github.com/argoproj/argo-cd/v2/common.gitTreeState={{ .Env.GIT_TREE_STATE }}
- -X github.com/argoproj/argo-cd/v2/common.kubectlVersion={{ .Env.KUBECTL_VERSION }}
- -extldflags="-static"
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
- s390x
- ppc64le
ignore:
- goos: darwin
goarch: s390x
- goos: darwin
goarch: ppc64le
- goos: windows
goarch: s390x
- goos: windows
goarch: ppc64le
- goos: windows
goarch: arm64
archives:
- id: argocd-archive
builds:
- argocd-cli
name_template: |-
{{ .ProjectName }}-{{ .Os }}-{{ .Arch }}
format: binary
checksum:
name_template: 'cli_checksums.txt'
algorithm: sha256
release:
prerelease: auto
draft: false
header: |
## Quick Start
### Non-HA:
```shell
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/{{.Tag}}/manifests/install.yaml
```
### HA:
```shell
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/{{.Tag}}/manifests/ha/install.yaml
```
## Release Signatures and Provenance
All Argo CD container images are signed by cosign. A Provenance is generated for container images and CLI binaries which meet the SLSA Level 3 specifications. See the [documentation](https://argo-cd.readthedocs.io/en/stable/operator-manual/signed-release-assets) on how to verify.
## Upgrading
If upgrading from a different minor version, be sure to read the [upgrading](https://argo-cd.readthedocs.io/en/stable/operator-manual/upgrading/overview/) documentation.
footer: |
**Full Changelog**: https://github.com/argoproj/argo-cd/compare/{{ .PreviousTag }}...{{ .Tag }}
<a href="https://argoproj.github.io/cd/"><img src="https://raw.githubusercontent.com/argoproj/argo-site/master/content/pages/cd/gitops-cd.png" width="25%" ></a>
snapshot: #### To be removed for PR
name_template: "2.6.0"
changelog:
use:
github
sort: asc
abbrev: 0
groups: # Regex use RE2 syntax as defined here: https://github.com/google/re2/wiki/Syntax.
- title: 'Features'
regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$'
order: 100
- title: 'Bug fixes'
regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$'
order: 200
- title: 'Documentation'
regexp: '^.*?docs(\([[:word:]]+\))??!?:.+$'
order: 300
- title: 'Dependency updates'
regexp: '^.*?(feat|fix|chore)\(deps?.+\)!?:.+$'
order: 400
- title: 'Other work'
order: 999
filters:
exclude:
- '^test:'
- '^.*?Bump(\([[:word:]]+\))?.+$'
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json

View File

@@ -4,8 +4,4 @@ mkdocs:
fail_on_warning: false
python:
install:
- requirements: docs/requirements.txt
build:
os: "ubuntu-22.04"
tools:
python: "3.7"
- requirements: docs/requirements.txt

View File

@@ -1,10 +1,10 @@
ARG BASE_IMAGE=docker.io/library/ubuntu:22.04@sha256:0bced47fffa3361afa981854fcabcd4577cd43cebbb808cea2b1f33a3dd7f508
ARG BASE_IMAGE=docker.io/library/ubuntu:22.04
####################################################################################################
# Builder image
# 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.20.10@sha256:077ff85b374b23916b4b41835e242e5a3ddad9fc537ea7e980f230431747d245 AS builder
FROM docker.io/library/golang:1.18 AS builder
RUN echo 'deb http://deb.debian.org/debian buster-backports main' >> /etc/apt/sources.list
@@ -36,8 +36,6 @@ RUN ./install.sh helm-linux && \
####################################################################################################
FROM $BASE_IMAGE AS argocd-base
LABEL org.opencontainers.image.source="https://github.com/argoproj/argo-cd"
USER root
ENV ARGOCD_USER_ID=999
@@ -83,7 +81,7 @@ WORKDIR /home/argocd
####################################################################################################
# Argo CD UI stage
####################################################################################################
FROM --platform=$BUILDPLATFORM docker.io/library/node:18.15.0@sha256:8d9a875ee427897ef245302e31e2319385b092f1c3368b497e89790f240368f5 AS argocd-ui
FROM --platform=$BUILDPLATFORM docker.io/library/node:12.18.4 AS argocd-ui
WORKDIR /src
COPY ["ui/package.json", "ui/yarn.lock", "./"]
@@ -101,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.20.10@sha256:077ff85b374b23916b4b41835e242e5a3ddad9fc537ea7e980f230431747d245 AS argocd-build
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.18 AS argocd-build
WORKDIR /go/src/github.com/argoproj/argo-cd
@@ -132,4 +130,3 @@ RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-server && \
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-k8s-auth
USER $ARGOCD_USER_ID
ENTRYPOINT ["/usr/bin/tini", "--"]

View File

@@ -64,20 +64,13 @@ else
DOCKER_SRC_MOUNT="$(PWD):/go/src/github.com/argoproj/argo-cd$(VOLUME_MOUNT)"
endif
# User and group IDs to map to the test container
CONTAINER_UID=$(shell id -u)
CONTAINER_GID=$(shell id -g)
# Set SUDO to sudo to run privileged commands with sudo
SUDO?=
# Runs any command in the argocd-test-utils container in server mode
# Server mode container will start with uid 0 and drop privileges during runtime
define run-in-test-server
$(SUDO) docker run --rm -it \
docker run --rm -it \
--name argocd-test-server \
-u $(CONTAINER_UID):$(CONTAINER_GID) \
-e USER_ID=$(CONTAINER_UID) \
-u $(shell id -u):$(shell id -g) \
-e USER_ID=$(shell id -u) \
-e HOME=/home/user \
-e GOPATH=/go \
-e GOCACHE=/tmp/go-build-cache \
@@ -105,9 +98,9 @@ endef
# Runs any command in the argocd-test-utils container in client mode
define run-in-test-client
$(SUDO) docker run --rm -it \
docker run --rm -it \
--name argocd-test-client \
-u $(CONTAINER_UID):$(CONTAINER_GID) \
-u $(shell id -u):$(shell id -g) \
-e HOME=/home/user \
-e GOPATH=/go \
-e ARGOCD_E2E_K3S=$(ARGOCD_E2E_K3S) \
@@ -126,7 +119,7 @@ endef
#
define exec-in-test-server
$(SUDO) docker exec -it -u $(CONTAINER_UID):$(CONTAINER_GID) -e ARGOCD_E2E_RECORD=$(ARGOCD_E2E_RECORD) -e ARGOCD_E2E_K3S=$(ARGOCD_E2E_K3S) argocd-test-server $(1)
docker exec -it -u $(shell id -u):$(shell id -g) -e ARGOCD_E2E_RECORD=$(ARGOCD_E2E_RECORD) -e ARGOCD_E2E_K3S=$(ARGOCD_E2E_K3S) argocd-test-server $(1)
endef
PATH:=$(PATH):$(PWD)/hack
@@ -219,7 +212,7 @@ clidocsgen: ensure-gopath
.PHONY: codegen-local
codegen-local: ensure-gopath mod-vendor-local gogen protogen clientgen openapigen clidocsgen manifests-local notification-docs notification-catalog
codegen-local: ensure-gopath mod-vendor-local notification-docs notification-catalog gogen protogen clientgen openapigen clidocsgen manifests-local
rm -rf vendor/
.PHONY: codegen
@@ -232,11 +225,11 @@ cli: test-tools-image
.PHONY: cli-local
cli-local: clean-debug
CGO_ENABLED=0 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd
CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd
.PHONY: gen-resources-cli-local
gen-resources-cli-local: clean-debug
CGO_ENABLED=0 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${GEN_RESOURCES_CLI_NAME} ./hack/gen-resources/cmd
CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${GEN_RESOURCES_CLI_NAME} ./hack/gen-resources/cmd
.PHONY: release-cli
release-cli: clean-debug build-ui
@@ -251,8 +244,8 @@ release-cli: clean-debug build-ui
.PHONY: test-tools-image
test-tools-image:
ifndef SKIP_TEST_TOOLS_IMAGE
$(SUDO) docker build --build-arg UID=$(CONTAINER_UID) -t $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE) -f test/container/Dockerfile .
$(SUDO) docker tag $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE) $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG)
docker build --build-arg UID=$(shell id -u) -t $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE) -f test/container/Dockerfile .
docker tag $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE) $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG)
endif
.PHONY: manifests-local
@@ -266,19 +259,19 @@ manifests: test-tools-image
# consolidated binary for cli, util, server, repo-server, controller
.PHONY: argocd-all
argocd-all: clean-debug
CGO_ENABLED=0 GOOS=${GOOS} GOARCH=${GOARCH} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${BIN_NAME} ./cmd
CGO_ENABLED=0 GOOS=${GOOS} GOARCH=${GOARCH} go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${BIN_NAME} ./cmd
.PHONY: server
server: clean-debug
CGO_ENABLED=0 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-server ./cmd
CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-server ./cmd
.PHONY: repo-server
repo-server:
CGO_ENABLED=0 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-repo-server ./cmd
CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-repo-server ./cmd
.PHONY: controller
controller:
CGO_ENABLED=0 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-application-controller ./cmd
CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-application-controller ./cmd
.PHONY: build-ui
build-ui:
@@ -294,7 +287,7 @@ ifeq ($(DEV_IMAGE), true)
IMAGE_TAG="dev-$(shell git describe --always --dirty)"
image: build-ui
DOCKER_BUILDKIT=1 docker build --platform=linux/amd64 -t argocd-base --target argocd-base .
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd ./cmd
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd ./cmd
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-server
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-application-controller
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-repo-server
@@ -333,7 +326,7 @@ mod-vendor: test-tools-image
mod-vendor-local: mod-download-local
go mod vendor
# Deprecated - replace by install-tools-local
# Deprecated - replace by install-local-tools
.PHONY: install-lint-tools
install-lint-tools:
./hack/install.sh lint-tools
@@ -368,7 +361,7 @@ build: test-tools-image
# Build all Go code (local version)
.PHONY: build-local
build-local:
GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v `go list ./... | grep -v 'resource_customizations\|test/e2e'`
go build -v `go list ./... | grep -v 'resource_customizations\|test/e2e'`
# Run all unit tests
#
@@ -579,7 +572,7 @@ list:
.PHONY: applicationset-controller
applicationset-controller:
GODEBUG="tarinsecurepath=0,zipinsecurepath=0" CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-applicationset-controller ./cmd
CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-applicationset-controller ./cmd
.PHONY: checksums
checksums:
@@ -596,55 +589,3 @@ snyk-non-container-tests:
.PHONY: snyk-report
snyk-report:
./hack/snyk-report.sh $(target_branch)
.PHONY: help
help:
@echo 'Note: Generally an item w/ (-local) will run inside docker unless you use the -local variant'
@echo
@echo 'Common targets'
@echo
@echo 'all -- make cli and image'
@echo
@echo 'components:'
@echo ' applicationset-controller -- applicationset controller'
@echo ' cli(-local) -- argocd cli program'
@echo ' controller -- controller (orchestrator)'
@echo ' repo-server -- repo server (manage repository instances)'
@echo ' server -- argocd web application'
@echo
@echo 'build:'
@echo ' image -- make image of the following items'
@echo ' build(-local) -- compile go'
@echo ' build-docs(-local) -- build docs'
@echo ' build-ui -- compile typescript'
@echo
@echo 'run:'
@echo ' run -- run the components locally'
@echo ' serve-docs(-local) -- expose the documents for viewing in a browser'
@echo
@echo 'release:'
@echo ' release-cli'
@echo ' release-precheck'
@echo ' checksums'
@echo
@echo 'docs:'
@echo ' build-docs(-local)'
@echo ' serve-docs(-local)'
@echo ' notification-docs'
@echo ' clidocsgen'
@echo
@echo 'testing:'
@echo ' test(-local)'
@echo ' start-e2e(-local)'
@echo ' test-e2e(-local)'
@echo ' test-race(-local)'
@echo
@echo 'debug:'
@echo ' list -- list all make targets'
@echo ' install-tools-local -- install all the tools below'
@echo ' install-lint-tools(-local)'
@echo
@echo 'codegen:'
@echo ' codegen(-local) -- if using -local, run the following targets first'
@echo ' install-codegen-tools-local -- run this to install the codegen tools'
@echo ' install-go-tools-local -- run this to install go libraries for codegen'

1
OWNERS
View File

@@ -29,4 +29,3 @@ reviewers:
- saumeya
- zachaller
- 34fathombelow
- alexef

View File

@@ -1,12 +1,12 @@
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} --otlp-address=${ARGOCD_OTLP_ADDRESS} --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_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server $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} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}"
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} --otlp-address=${ARGOCD_OTLP_ADDRESS} --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_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server $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} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}"
dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v2/cmd gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:$(grep "image: ghcr.io/dexidp/dex" manifests/base/dex/argocd-dex-server-deployment.yaml | cut -d':' -f3) dex serve /dex.yaml"
redis: bash -c "if [ \"$ARGOCD_REDIS_LOCAL\" = 'true' ]; then redis-server --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; else docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} redis:$(grep "image: redis" manifests/base/redis/argocd-redis-deployment.yaml | cut -d':' -f3) --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; fi"
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_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-repo-server ARGOCD_GPG_ENABLED=${ARGOCD_GPG_ENABLED:-false} $COMMAND --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --otlp-address=${ARGOCD_OTLP_ADDRESS}"
cmp-server: [ "$ARGOCD_E2E_TEST" = 'true' ] && exit 0 || [ "$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-cmp-server ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} $COMMAND --config-dir-path ./test/cmp --loglevel debug --otlp-address=${ARGOCD_OTLP_ADDRESS}"
redis: bash -c "if [ \"$ARGOCD_REDIS_LOCAL\" == 'true' ]; then redis-server --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; else docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} redis:$(grep "image: redis" manifests/base/redis/argocd-redis-deployment.yaml | cut -d':' -f3) --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; fi"
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_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-repo-server ARGOCD_GPG_ENABLED=${ARGOCD_GPG_ENABLED:-false} $COMMAND --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --otlp-address=${ARGOCD_OTLP_ADDRESS}"
cmp-server: [ "$ARGOCD_E2E_TEST" == 'true' ] && exit 0 || [ "$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-cmp-server ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} $COMMAND --config-dir-path ./test/cmp --loglevel debug --otlp-address=${ARGOCD_OTLP_ADDRESS}"
ui: sh -c 'cd ui && ${ARGOCD_E2E_YARN_CMD:-yarn} start'
git-server: test/fixture/testrepos/start-git.sh
helm-registry: test/fixture/testrepos/start-helm-registry.sh
dev-mounter: [[ "$ARGOCD_E2E_TEST" != "true" ]] && go run hack/dev-mounter/main.go --configmap argocd-ssh-known-hosts-cm=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} --configmap argocd-tls-certs-cm=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} --configmap argocd-gpg-keys-cm=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source}
applicationset-controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_ASK_PASS_SOCK=/tmp/applicationset-ask-pass.sock ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-applicationset-controller $COMMAND --loglevel debug --metrics-addr localhost:12345 --probe-addr localhost:12346 --argocd-repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
notification: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_BINARY_NAME=argocd-notifications $COMMAND --loglevel debug"
applicationset-controller: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_ASK_PASS_SOCK=/tmp/applicationset-ask-pass.sock ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-applicationset-controller $COMMAND --loglevel debug --metrics-addr localhost:12345 --probe-addr localhost:12346 --argocd-repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
notification: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_BINARY_NAME=argocd-notifications $COMMAND --loglevel debug"

View File

@@ -1,18 +1,6 @@
**Releases:**
[![Release Version](https://img.shields.io/github/v/release/argoproj/argo-cd?label=argo-cd)](https://github.com/argoproj/argo-cd/releases/latest)
[![Integration tests](https://github.com/argoproj/argo-cd/workflows/Integration%20tests/badge.svg?branch=master)](https://github.com/argoproj/argo-cd/actions?query=workflow%3A%22Integration+tests%22) [![slack](https://img.shields.io/badge/slack-argoproj-brightgreen.svg?logo=slack)](https://argoproj.github.io/community/join-slack) [![codecov](https://codecov.io/gh/argoproj/argo-cd/branch/master/graph/badge.svg)](https://codecov.io/gh/argoproj/argo-cd) [![Release Version](https://img.shields.io/github/v/release/argoproj/argo-cd?label=argo-cd)](https://github.com/argoproj/argo-cd/releases/latest) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/4486/badge)](https://bestpractices.coreinfrastructure.org/projects/4486) [![Twitter Follow](https://img.shields.io/twitter/follow/argoproj?style=social)](https://twitter.com/argoproj)
[![Artifact HUB](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/argo-cd)](https://artifacthub.io/packages/helm/argo/argo-cd)
**Code:**
[![Integration tests](https://github.com/argoproj/argo-cd/workflows/Integration%20tests/badge.svg?branch=master)](https://github.com/argoproj/argo-cd/actions?query=workflow%3A%22Integration+tests%22)
[![codecov](https://codecov.io/gh/argoproj/argo-cd/branch/master/graph/badge.svg)](https://codecov.io/gh/argoproj/argo-cd)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/4486/badge)](https://bestpractices.coreinfrastructure.org/projects/4486)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/argoproj/argo-cd/badge)](https://api.securityscorecards.dev/projects/github.com/argoproj/argo-cd)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fargoproj%2Fargo-cd.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fargoproj%2Fargo-cd?ref=badge_shield)
**Social:**
[![Twitter Follow](https://img.shields.io/twitter/follow/argoproj?style=social)](https://twitter.com/argoproj)
[![Slack](https://img.shields.io/badge/slack-argoproj-brightgreen.svg?logo=slack)](https://argoproj.github.io/community/join-slack)
# Argo CD - Declarative Continuous Delivery for Kubernetes
## What is Argo CD?
@@ -81,7 +69,7 @@ Participation in the Argo CD project is governed by the [CNCF Code of Conduct](h
1. [Applied GitOps with Argo CD](https://thenewstack.io/applied-gitops-with-argocd/)
1. [Solving configuration drift using GitOps with Argo CD](https://www.cncf.io/blog/2020/12/17/solving-configuration-drift-using-gitops-with-argo-cd/)
1. [Decentralized GitOps over environments](https://blogs.sap.com/2021/05/06/decentralized-gitops-over-environments/)
1. [How GitOps and Operators mark the rise of Infrastructure-As-Software](https://paytmlabs.com/blog/2021/10/how-to-improve-operational-work-with-operators-and-gitops/)
1. [Getting Started with ArgoCD for GitOps Deployments](https://youtu.be/AvLuplh1skA)
1. [Using Argo CD & Datree for Stable Kubernetes CI/CD Deployments](https://youtu.be/17894DTru2Y)
1. [How to create Argo CD Applications Automatically using ApplicationSet? "Automation of GitOps"](https://amralaayassen.medium.com/how-to-create-argocd-applications-automatically-using-applicationset-automation-of-the-gitops-59455eaf4f72)

View File

@@ -1,6 +1,6 @@
# Security Policy for Argo CD
Version: **v1.5 (2023-03-06)**
Version: **v1.4 (2022-01-23)**
## Preface
@@ -35,11 +35,13 @@ impact on Argo CD before opening an issue at least roughly.
## Supported Versions
We currently support the last 3 minor versions of Argo CD with security and bug fixes.
We currently support the most recent release (`N`, e.g. `1.8`) and the release
previous to the most recent one (`N-1`, e.g. `1.7`). With the release of
`N+1`, `N-1` drops out of support and `N` becomes `N-1`.
We regularly perform patch releases (e.g. `1.8.5` and `1.7.12`) for the
supported versions, which will contain fixes for security vulnerabilities and
important bugs. Prior releases might receive critical security fixes on best
important bugs. Prior releases might receive critical security fixes on a best
effort basis, however, it cannot be guaranteed that security fixes get
back-ported to these unsupported versions.
@@ -59,28 +61,14 @@ and disclosure with you. Sometimes, it might take a little longer for us to
react (e.g. out of office conditions), so please bear with us in these cases.
We will publish security advisories using the
[GitHub Security Advisories](https://github.com/argoproj/argo-cd/security/advisories)
feature to keep our community well-informed, and will credit you for your
[Git Hub Security Advisories](https://github.com/argoproj/argo-cd/security/advisories)
feature to keep our community well informed, and will credit you for your
findings (unless you prefer to stay anonymous, of course).
Please report vulnerabilities by e-mail to the following address:
* cncf-argo-security@lists.cncf.io
## Internet Bug Bounty collaboration
We're happy to announce that the Argo project is collaborating with the great
folks over at
[Hacker One](https://hackerone.com/) and their
[Internet Bug Bounty program](https://hackerone.com/ibb)
to reward the awesome people who find security vulnerabilities in the four
main Argo projects (CD, Events, Rollouts and Workflows) and then work with
us to fix and disclose them in a responsible manner.
If you report a vulnerability to us as outlined in this security policy, we
will work together with you to find out whether your finding is eligible for
claiming a bounty, and also on how to claim it.
## Securing your Argo CD Instance
See the [operator manual security page](docs/operator-manual/security.md) for

View File

@@ -1,7 +1,7 @@
# Defined below are the security contacts for this repo.
#
# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE
# INSTRUCTIONS AT https://github.com/argoproj/argo-cd/security/policy
# INSTRUCTIONS AT https://argo-cd.readthedocs.io/en/latest/security_considerations/#reporting-vulnerabilities
alexmt
edlee2121

View File

@@ -11,7 +11,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Adevinta](https://www.adevinta.com/)
1. [Adfinis](https://adfinis.com)
1. [Adventure](https://jp.adventurekk.com/)
1. [Adyen](https://www.adyen.com)
1. [AirQo](https://airqo.net/)
1. [Akuity](https://akuity.io/)
1. [Alibaba Group](https://www.alibabagroup.com/)
@@ -24,7 +23,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Arctiq Inc.](https://www.arctiq.ca)
1. [ARZ Allgemeines Rechenzentrum GmbH](https://www.arz.at/)
1. [Axual B.V.](https://axual.com)
1. [Back Market](https://www.backmarket.com)
1. [Baloise](https://www.baloise.com)
1. [BCDevExchange DevOps Platform](https://bcdevexchange.org/DevOpsPlatform)
1. [Beat](https://thebeat.co/en/)
@@ -33,7 +31,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [BigPanda](https://bigpanda.io)
1. [BioBox Analytics](https://biobox.io)
1. [BMW Group](https://www.bmwgroup.com/)
1. [PT Boer Technology (Btech)](https://btech.id/)
1. [Boozt](https://www.booztgroup.com/)
1. [Boticario](https://www.boticario.com.br/)
1. [Bulder Bank](https://bulderbank.no)
@@ -47,9 +44,7 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Chargetrip](https://chargetrip.com)
1. [Chime](https://www.chime.com)
1. [Cisco ET&I](https://eti.cisco.com/)
1. [Cloud Posse](https://www.cloudposse.com/)
1. [Cloud Scale](https://cloudscaleinc.com/)
1. [Cloudmate](https://cloudmt.co.kr/)
1. [Cobalt](https://www.cobalt.io/)
1. [Codefresh](https://www.codefresh.io/)
1. [Codility](https://www.codility.com/)
@@ -65,7 +60,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Deutsche Telekom AG](https://telekom.com)
1. [Devopsi - Poland Software/DevOps Consulting](https://devopsi.pl/)
1. [Devtron Labs](https://github.com/devtron-labs/devtron)
1. [DigitalOcean](https://www.digitalocean.com)
1. [Divistant](https://divistant.com)
1. [Doximity](https://www.doximity.com/)
1. [EDF Renewables](https://www.edf-re.com/)
@@ -78,7 +72,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Energisme](https://energisme.com/)
1. [enigmo](https://enigmo.co.jp/)
1. [Envoy](https://envoy.com/)
1. [Farfetch](https://www.farfetch.com)
1. [Faro](https://www.faro.com/)
1. [Fave](https://myfave.com)
1. [Flip](https://flip.id)
@@ -96,12 +89,10 @@ Currently, the following organizations are **officially** using Argo CD:
1. [gloat](https://gloat.com/)
1. [GLOBIS](https://globis.com)
1. [Glovo](https://www.glovoapp.com)
1. [GlueOps](https://glueops.dev)
1. [GMETRI](https://gmetri.com/)
1. [Gojek](https://www.gojek.io/)
1. [Greenpass](https://www.greenpass.com.br/)
1. [Gridfuse](https://gridfuse.com/)
1. [Groww](https://groww.in)
1. [Grupo MasMovil](https://grupomasmovil.com/en/)
1. [Handelsbanken](https://www.handelsbanken.se)
1. [Healy](https://www.healyworld.net)
@@ -138,7 +129,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Liatrio](https://www.liatrio.com)
1. [Lightricks](https://www.lightricks.com/)
1. [LINE](https://linecorp.com/en/)
1. [Loom](https://www.loom.com/)
1. [Lytt](https://www.lytt.co/)
1. [Magic Leap](https://www.magicleap.com/)
1. [Majid Al Futtaim](https://www.majidalfuttaim.com/)
@@ -164,19 +154,15 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Nextdoor](https://nextdoor.com/)
1. [Nikkei](https://www.nikkei.co.jp/nikkeiinfo/en/)
1. [Nitro](https://gonitro.com)
1. [NYCU, CS IT Center](https://it.cs.nycu.edu.tw)
1. [Objective](https://www.objective.com.br/)
1. [OCCMundial](https://occ.com.mx)
1. [Octadesk](https://octadesk.com)
1. [omegaUp](https://omegaUp.com)
1. [Omni](https://omni.se/)
1. [openEuler](https://openeuler.org)
1. [openGauss](https://opengauss.org/)
1. [OpenGov](https://opengov.com)
1. [openLooKeng](https://openlookeng.io)
1. [OpenSaaS Studio](https://opensaas.studio)
1. [Opensurvey](https://www.opensurvey.co.kr/)
1. [OpsMx](https://opsmx.io)
1. [OpsVerse](https://opsverse.io)
1. [Optoro](https://www.optoro.com/)
1. [Orbital Insight](https://orbitalinsight.com/)
@@ -187,11 +173,9 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Patreon](https://www.patreon.com/)
1. [PayPay](https://paypay.ne.jp/)
1. [Peloton Interactive](https://www.onepeloton.com/)
1. [PGS](https://www.pgs.com)
1. [Pigment](https://www.gopigment.com/)
1. [Pipefy](https://www.pipefy.com/)
1. [Pismo](https://pismo.io/)
1. [Platform9 Systems](https://platform9.com/)
1. [Polarpoint.io](https://polarpoint.io)
1. [PostFinance](https://github.com/postfinance)
1. [Preferred Networks](https://preferred.jp/en/)
@@ -204,8 +188,6 @@ 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. [Reenigne Cloud](https://reenigne.ca)
1. [reev.com](https://www.reev.com/)
1. [RightRev](https://rightrev.com/)
1. [Rise](https://www.risecard.eu/)
@@ -215,14 +197,11 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Saildrone](https://www.saildrone.com/)
1. [Saloodo! GmbH](https://www.saloodo.com)
1. [Sap Labs](http://sap.com)
1. [Sauce Labs](https://saucelabs.com/)
1. [Schwarz IT](https://jobs.schwarz/it-mission)
1. [SI Analytics](https://si-analytics.ai)
1. [Skit](https://skit.ai/)
1. [Skyscanner](https://www.skyscanner.net/)
1. [Smart Pension](https://www.smartpension.co.uk/)
1. [Smilee.io](https://smilee.io)
1. [Smood.ch](https://www.smood.ch/)
1. [Snapp](https://snapp.ir/)
1. [Snyk](https://snyk.io/)
1. [Softway Medical](https://www.softwaymedical.fr/)
@@ -259,8 +238,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [ungleich.ch](https://ungleich.ch/)
1. [Unifonic Inc](https://www.unifonic.com/)
1. [Universidad Mesoamericana](https://www.umes.edu.gt/)
1. [Urbantz](https://urbantz.com/)
1. [Vectra](https://www.vectra.ai)
1. [Viaduct](https://www.viaduct.ai/)
1. [Vinted](https://vinted.com/)
1. [Virtuo](https://www.govirtuo.com/)

View File

@@ -1 +1 @@
2.7.18
2.6.4

View File

@@ -17,7 +17,6 @@ package controllers
import (
"context"
"fmt"
"reflect"
"time"
log "github.com/sirupsen/logrus"
@@ -30,12 +29,9 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/source"
"github.com/argoproj/argo-cd/v2/applicationset/generators"
@@ -46,8 +42,6 @@ import (
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
argoutil "github.com/argoproj/argo-cd/v2/util/argo"
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
)
const (
@@ -59,7 +53,7 @@ const (
)
var (
defaultPreservedAnnotations = []string{
preservedAnnotations = []string{
NotifiedAnnotationKey,
argov1alpha1.AnnotationKeyRefresh,
}
@@ -148,30 +142,19 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
// appSyncMap tracks which apps will be synced during this reconciliation.
appSyncMap := map[string]bool{}
if r.EnableProgressiveSyncs {
if applicationSetInfo.Spec.Strategy == nil && len(applicationSetInfo.Status.ApplicationStatus) > 0 {
// If appset used progressive sync but stopped, clean up the progressive sync application statuses
log.Infof("Removing %v unnecessary AppStatus entries from ApplicationSet %v", len(applicationSetInfo.Status.ApplicationStatus), applicationSetInfo.Name)
if r.EnableProgressiveSyncs && applicationSetInfo.Spec.Strategy != nil {
applications, err := r.getCurrentApplications(ctx, applicationSetInfo)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to get current applications for application set: %w", err)
}
err := r.setAppSetApplicationStatus(ctx, &applicationSetInfo, []argov1alpha1.ApplicationSetApplicationStatus{})
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to clear previous AppSet application statuses for %v: %w", applicationSetInfo.Name, err)
}
} else if applicationSetInfo.Spec.Strategy != nil {
// appset uses progressive sync
applications, err := r.getCurrentApplications(ctx, applicationSetInfo)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to get current applications for application set: %w", err)
}
for _, app := range applications {
appMap[app.Name] = app
}
for _, app := range applications {
appMap[app.Name] = app
}
appSyncMap, err = r.performProgressiveSyncs(ctx, applicationSetInfo, applications, desiredApplications, appMap)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to perform progressive sync reconciliation for application set: %w", err)
}
appSyncMap, err = r.performProgressiveSyncs(ctx, applicationSetInfo, applications, desiredApplications, appMap)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to perform progressive sync reconciliation for application set: %w", err)
}
}
@@ -288,6 +271,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
}
requeueAfter := r.getMinRequeueAfter(&applicationSetInfo)
logCtx.WithField("requeueAfter", requeueAfter).Info("end reconcile")
if len(validateErrors) == 0 {
if err := r.setApplicationSetStatusCondition(ctx,
@@ -301,13 +285,8 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
); err != nil {
return ctrl.Result{}, err
}
} else if requeueAfter == time.Duration(0) {
// Ensure that the request is requeued if there are validation errors.
requeueAfter = ReconcileRequeueOnValidationError
}
logCtx.WithField("requeueAfter", requeueAfter).Info("end reconcile")
return ctrl.Result{
RequeueAfter: requeueAfter,
}, nil
@@ -533,7 +512,7 @@ func (r *ApplicationSetReconciler) generateApplications(applicationSetInfo argov
return res, applicationSetReason, firstError
}
func (r *ApplicationSetReconciler) SetupWithManager(mgr ctrl.Manager, enableProgressiveSyncs bool) error {
func (r *ApplicationSetReconciler) SetupWithManager(mgr ctrl.Manager) error {
if err := mgr.GetFieldIndexer().IndexField(context.TODO(), &argov1alpha1.Application{}, ".metadata.controller", func(rawObj client.Object) []string {
// grab the job object, extract the owner...
app := rawObj.(*argov1alpha1.Application)
@@ -552,11 +531,9 @@ func (r *ApplicationSetReconciler) SetupWithManager(mgr ctrl.Manager, enableProg
return fmt.Errorf("error setting up with manager: %w", err)
}
ownsHandler := getOwnsHandlerPredicates(enableProgressiveSyncs)
return ctrl.NewControllerManagedBy(mgr).
For(&argov1alpha1.ApplicationSet{}).
Owns(&argov1alpha1.Application{}, builder.WithPredicates(ownsHandler)).
Owns(&argov1alpha1.Application{}).
Watches(
&source.Kind{Type: &corev1.Secret{}},
&clusterSecretEventHandler{
@@ -580,16 +557,13 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context,
appLog := log.WithFields(log.Fields{"app": generatedApp.Name, "appSet": applicationSet.Name})
generatedApp.Namespace = applicationSet.Namespace
// Normalize to avoid fighting with the application controller.
generatedApp.Spec = *argoutil.NormalizeApplicationSpec(&generatedApp.Spec)
found := &argov1alpha1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: generatedApp.Name,
Namespace: generatedApp.Namespace,
},
TypeMeta: metav1.TypeMeta{
Kind: application.ApplicationKind,
Kind: "Application",
APIVersion: "argoproj.io/v1alpha1",
},
}
@@ -603,15 +577,9 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context,
found.Operation = generatedApp.Operation
}
preservedAnnotations := make([]string, 0)
if applicationSet.Spec.PreservedFields != nil {
preservedAnnotations = append(preservedAnnotations, applicationSet.Spec.PreservedFields.Annotations...)
}
// Preserve specially treated argo cd annotations:
// * https://github.com/argoproj/applicationset/issues/180
// * https://github.com/argoproj/argo-cd/issues/10500
preservedAnnotations = append(preservedAnnotations, defaultPreservedAnnotations...)
for _, key := range preservedAnnotations {
if state, exists := found.ObjectMeta.Annotations[key]; exists {
if generatedApp.Annotations == nil {
@@ -869,21 +837,45 @@ func (r *ApplicationSetReconciler) buildAppDependencyList(ctx context.Context, a
selected := true // default to true, assuming the current Application is a match for the given step matchExpression
allNotInMatched := true // needed to support correct AND behavior between multiple NotIn MatchExpressions
notInUsed := false // since we default to allNotInMatched == true, track whether a NotIn expression was actually used
for _, matchExpression := range step.MatchExpressions {
if val, ok := app.Labels[matchExpression.Key]; ok {
valueMatched := labelMatchedExpression(val, matchExpression)
if matchExpression.Operator == "In" {
if val, ok := app.Labels[matchExpression.Key]; ok {
valueMatched := labelMatchedExpression(val, matchExpression)
if !valueMatched { // none of the matchExpression values was a match with the Application'ss labels
selected = false
if !valueMatched { // none of the matchExpression values was a match with the Application'ss labels
selected = false
break
}
} else {
selected = false // no matching label key with In means this Application will not be included in the current step
break
}
} else if matchExpression.Operator == "In" {
selected = false // no matching label key with "In" operator means this Application will not be included in the current step
} else if matchExpression.Operator == "NotIn" {
notInUsed = true // a NotIn selector was used in this matchExpression
if val, ok := app.Labels[matchExpression.Key]; ok {
valueMatched := labelMatchedExpression(val, matchExpression)
if !valueMatched { // none of the matchExpression values was a match with the Application's labels
allNotInMatched = false
}
} else {
allNotInMatched = false // no matching label key with NotIn means this Application may still be included in the current step
}
} else { // handle invalid operator selection
log.Warnf("skipping AppSet rollingUpdate step Application selection for %q, invalid matchExpression operator provided: %q ", applicationSet.Name, matchExpression.Operator)
selected = false
break
}
}
if notInUsed && allNotInMatched { // check if all NotIn Expressions matched, if so exclude this Application
selected = false
}
if selected {
appDependencyList[i] = append(appDependencyList[i], app.Name)
if val, ok := appStepMap[app.Name]; ok {
@@ -899,20 +891,11 @@ func (r *ApplicationSetReconciler) buildAppDependencyList(ctx context.Context, a
}
func labelMatchedExpression(val string, matchExpression argov1alpha1.ApplicationMatchExpression) bool {
if matchExpression.Operator != "In" && matchExpression.Operator != "NotIn" {
log.Errorf("skipping AppSet rollingUpdate step Application selection, invalid matchExpression operator provided: %q ", matchExpression.Operator)
return false
}
// if operator == In, default to false
// if operator == NotIn, default to true
valueMatched := matchExpression.Operator == "NotIn"
valueMatched := false
for _, value := range matchExpression.Values {
if val == value {
// first "In" match returns true
// first "NotIn" match returns false
return matchExpression.Operator == "In"
valueMatched = true
break
}
}
return valueMatched
@@ -1045,12 +1028,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con
}
if currentAppStatus.Status == "Pending" {
// check for successful syncs started less than 10s before the Application transitioned to Pending
// this covers race conditions where syncs initiated by RollingSync miraculously have a sync time before the transition to Pending state occurred (could be a few seconds)
if operationPhaseString == "Succeeded" && app.Status.OperationState.StartedAt.Add(time.Duration(10)*time.Second).After(currentAppStatus.LastTransitionTime.Time) {
if !app.Status.OperationState.StartedAt.After(currentAppStatus.LastTransitionTime.Time) {
log.Warnf("Application %v was synced less than 10s prior to entering Pending status, we'll assume the AppSet controller triggered this sync and update its status to Progressing", app.Name)
}
if operationPhaseString == "Succeeded" && app.Status.OperationState.StartedAt.After(currentAppStatus.LastTransitionTime.Time) {
log.Infof("Application %v has completed a sync successfully, updating its ApplicationSet status to Progressing", app.Name)
currentAppStatus.LastTransitionTime = &now
currentAppStatus.Status = "Progressing"
@@ -1228,30 +1206,30 @@ func findApplicationStatusIndex(appStatuses []argov1alpha1.ApplicationSetApplica
// with any new/changed Application statuses.
func (r *ApplicationSetReconciler) setAppSetApplicationStatus(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet, applicationStatuses []argov1alpha1.ApplicationSetApplicationStatus) error {
needToUpdateStatus := false
if len(applicationStatuses) != len(applicationSet.Status.ApplicationStatus) {
needToUpdateStatus = true
} else {
for i := range applicationStatuses {
appStatus := applicationStatuses[i]
idx := findApplicationStatusIndex(applicationSet.Status.ApplicationStatus, appStatus.Application)
if idx == -1 {
needToUpdateStatus = true
break
}
currentStatus := applicationSet.Status.ApplicationStatus[idx]
if currentStatus.Message != appStatus.Message || currentStatus.Status != appStatus.Status || currentStatus.Step != appStatus.Step {
needToUpdateStatus = true
break
}
for i := range applicationStatuses {
appStatus := applicationStatuses[i]
idx := findApplicationStatusIndex(applicationSet.Status.ApplicationStatus, appStatus.Application)
if idx == -1 {
needToUpdateStatus = true
break
}
currentStatus := applicationSet.Status.ApplicationStatus[idx]
if currentStatus.Message != appStatus.Message || currentStatus.Status != appStatus.Status {
needToUpdateStatus = true
break
}
}
if needToUpdateStatus {
// fetch updated Application Set object before updating it
namespacedName := types.NamespacedName{Namespace: applicationSet.Namespace, Name: applicationSet.Name}
if err := r.Get(ctx, namespacedName, applicationSet); err != nil {
if client.IgnoreNotFound(err) != nil {
return nil
}
return fmt.Errorf("error fetching updated application set: %v", err)
}
// rebuild ApplicationStatus from scratch, we don't need any previous status history
applicationSet.Status.ApplicationStatus = []argov1alpha1.ApplicationSetApplicationStatus{}
for i := range applicationStatuses {
applicationSet.Status.SetApplicationStatus(applicationStatuses[i])
}
@@ -1334,73 +1312,4 @@ func syncApplication(application argov1alpha1.Application, prune bool) (argov1al
return application, nil
}
func getOwnsHandlerPredicates(enableProgressiveSyncs bool) predicate.Funcs {
return predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool {
// if we are the owner and there is a create event, we most likely created it and do not need to
// re-reconcile
log.Debugln("received create event from owning an application")
return false
},
DeleteFunc: func(e event.DeleteEvent) bool {
log.Debugln("received delete event from owning an application")
return true
},
UpdateFunc: func(e event.UpdateEvent) bool {
log.Debugln("received update event from owning an application")
appOld, isApp := e.ObjectOld.(*argov1alpha1.Application)
if !isApp {
return false
}
appNew, isApp := e.ObjectNew.(*argov1alpha1.Application)
if !isApp {
return false
}
requeue := shouldRequeueApplicationSet(appOld, appNew, enableProgressiveSyncs)
log.Debugf("requeue: %t caused by application %s\n", requeue, appNew.Name)
return requeue
},
GenericFunc: func(e event.GenericEvent) bool {
log.Debugln("received generic event from owning an application")
return true
},
}
}
// shouldRequeueApplicationSet determines when we want to requeue an ApplicationSet for reconciling based on an owned
// application change
// The applicationset controller owns a subset of the Application CR.
// We do not need to re-reconcile if parts of the application change outside the applicationset's control.
// An example being, Application.ApplicationStatus.ReconciledAt which gets updated by the application controller.
// Additionally, Application.ObjectMeta.ResourceVersion and Application.ObjectMeta.Generation which are set by K8s.
func shouldRequeueApplicationSet(appOld *argov1alpha1.Application, appNew *argov1alpha1.Application, enableProgressiveSyncs bool) bool {
if appOld == nil || appNew == nil {
return false
}
// the applicationset controller owns the application spec, labels, annotations, and finalizers on the applications
if !reflect.DeepEqual(appOld.Spec, appNew.Spec) ||
!reflect.DeepEqual(appOld.ObjectMeta.GetAnnotations(), appNew.ObjectMeta.GetAnnotations()) ||
!reflect.DeepEqual(appOld.ObjectMeta.GetLabels(), appNew.ObjectMeta.GetLabels()) ||
!reflect.DeepEqual(appOld.ObjectMeta.GetFinalizers(), appNew.ObjectMeta.GetFinalizers()) {
return true
}
// progressive syncs use the application status for updates. if they differ, requeue to trigger the next progression
if enableProgressiveSyncs {
if appOld.Status.Health.Status != appNew.Status.Health.Status || appOld.Status.Sync.Status != appNew.Status.Sync.Status {
return true
}
if appOld.Status.OperationState != nil && appNew.Status.OperationState != nil {
if appOld.Status.OperationState.Phase != appNew.Status.OperationState.Phase ||
appOld.Status.OperationState.StartedAt != appNew.Status.OperationState.StartedAt {
return true
}
}
}
return false
}
var _ handler.EventHandler = &clusterSecretEventHandler{}

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,6 @@ package controllers
import (
"context"
"fmt"
log "github.com/sirupsen/logrus"
@@ -47,6 +46,7 @@ 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,40 +73,6 @@ 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 {
@@ -116,42 +82,3 @@ 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,7 +6,6 @@ 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"
@@ -164,6 +163,7 @@ 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,348 +189,6 @@ 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

@@ -1,14 +0,0 @@
key:
components:
- name: component1
chart: podinfo
version: "6.3.2"
releaseName: component1
repoUrl: "https://stefanprodan.github.io/podinfo"
namespace: component1
- name: component2
chart: podinfo
version: "6.3.3"
releaseName: component2
repoUrl: "ghcr.io/stefanprodan/charts"
namespace: component2

View File

@@ -2,11 +2,12 @@ package generators
import (
"fmt"
"encoding/json"
"reflect"
"github.com/argoproj/argo-cd/v2/applicationset/utils"
"github.com/jeremywohl/flatten"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
@@ -24,12 +25,9 @@ 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) {
// This is a custom version of the `LabelSelectorAsSelector` that is in k8s.io/apimachinery. This has been copied
// verbatim from that package, with the difference that we do not have any restrictions on label values. This is done
// so that, among other things, we can match on cluster urls.
selector, err := utils.LabelSelectorAsSelector(requestedGenerator.Selector)
selector, err := metav1.LabelSelectorAsSelector(requestedGenerator.Selector)
if err != nil {
return nil, fmt.Errorf("error parsing label selector: %w", err)
}
@@ -74,17 +72,8 @@ func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, al
}
var filterParams []map[string]interface{}
for _, param := range params {
flatParam, err := flattenParameters(param)
if err != nil {
log.WithError(err).WithField("generator", g).
Error("error flattening params")
if firstError == nil {
firstError = err
}
continue
}
if requestedGenerator.Selector != nil && !selector.Matches(labels.Set(flatParam)) {
if requestedGenerator.Selector != nil && !selector.Matches(labels.Set(keepOnlyStringValues(param))) {
continue
}
filterParams = append(filterParams, param)
@@ -99,6 +88,18 @@ func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, al
return res, firstError
}
func keepOnlyStringValues(in map[string]interface{}) map[string]string {
var out map[string]string = map[string]string{}
for key, value := range in {
if _, ok := value.(string); ok {
out[key] = value.(string)
}
}
return out
}
func GetRelevantGenerators(requestedGenerator *argoprojiov1alpha1.ApplicationSetGenerator, generators map[string]Generator) []Generator {
var res []Generator
@@ -121,20 +122,6 @@ func GetRelevantGenerators(requestedGenerator *argoprojiov1alpha1.ApplicationSet
return res
}
func flattenParameters(in map[string]interface{}) (map[string]string, error) {
flat, err := flatten.Flatten(in, "", flatten.DotStyle)
if err != nil {
return nil, err
}
out := make(map[string]string, len(flat))
for k, v := range flat {
out[k] = fmt.Sprintf("%v", v)
}
return out, nil
}
func mergeGeneratorTemplate(g Generator, requestedGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetTemplate argoprojiov1alpha1.ApplicationSetTemplate) (argoprojiov1alpha1.ApplicationSetTemplate, error) {
// Make a copy of the value from `GetTemplate()` before merge, rather than copying directly into
// the provided parameter (which will touch the original resource object returned by client-go)
@@ -145,15 +132,27 @@ func mergeGeneratorTemplate(g Generator, requestedGenerator *argoprojiov1alpha1.
return *dest, err
}
// InterpolateGenerator allows interpolating the matrix's 2nd child generator with values from the 1st child generator
// Currently for Matrix Generator. 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) {
render := utils.Render{}
interpolatedGenerator, err := render.RenderGeneratorParams(requestedGenerator, params, useGoTemplate)
interpolatedGenerator := requestedGenerator.DeepCopy()
tmplBytes, err := json.Marshal(interpolatedGenerator)
if err != nil {
log.WithError(err).WithField("interpolatedGenerator", interpolatedGenerator).Error("error interpolating generator with other generator's parameter")
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)
if err != nil {
log.WithError(err).WithField("interpolatedGeneratorString", replacedTmplStr).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,11 +6,9 @@ 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"
@@ -75,9 +73,7 @@ func TestMatchValues(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: "set",
},
Spec: argoprojiov1alpha1.ApplicationSetSpec{
GoTemplate: false,
},
Spec: argoprojiov1alpha1.ApplicationSetSpec{},
}
results, err := Transform(argoprojiov1alpha1.ApplicationSetGenerator{
@@ -96,159 +92,8 @@ func TestMatchValues(t *testing.T) {
}
}
func TestMatchValuesGoTemplate(t *testing.T) {
testCases := []struct {
name string
elements []apiextensionsv1.JSON
selector *metav1.LabelSelector
expected []map[string]interface{}
}{
{
name: "no filter",
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url"}`)}},
selector: &metav1.LabelSelector{},
expected: []map[string]interface{}{{"cluster": "cluster", "url": "url"}},
},
{
name: "nil",
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url"}`)}},
selector: nil,
expected: []map[string]interface{}{{"cluster": "cluster", "url": "url"}},
},
{
name: "values.foo should be foo but is ignore element",
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url","values":{"foo":"bar"}}`)}},
selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"values.foo": "foo",
},
},
expected: []map[string]interface{}{},
},
{
name: "values.foo should be bar",
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url","values":{"foo":"bar"}}`)}},
selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"values.foo": "bar",
},
},
expected: []map[string]interface{}{{"cluster": "cluster", "url": "url", "values": map[string]interface{}{"foo": "bar"}}},
},
{
name: "values.0 should be bar",
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url","values":["bar"]}`)}},
selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"values.0": "bar",
},
},
expected: []map[string]interface{}{{"cluster": "cluster", "url": "url", "values": []interface{}{"bar"}}},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
var listGenerator = NewListGenerator()
var data = map[string]Generator{
"List": listGenerator,
}
applicationSetInfo := argov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "set",
},
Spec: argov1alpha1.ApplicationSetSpec{
GoTemplate: true,
},
}
results, err := Transform(argov1alpha1.ApplicationSetGenerator{
Selector: testCase.selector,
List: &argov1alpha1.ListGenerator{
Elements: testCase.elements,
Template: emptyTemplate(),
}},
data,
emptyTemplate(),
&applicationSetInfo, nil)
assert.NoError(t, err)
assert.ElementsMatch(t, testCase.expected, results[0].Params)
})
}
}
func TestTransForm(t *testing.T) {
testCases := []struct {
name string
selector *metav1.LabelSelector
expected []map[string]interface{}
}{
{
name: "server filter",
selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"server": "https://production-01.example.com"},
},
expected: []map[string]interface{}{{
"metadata.annotations.foo.argoproj.io": "production",
"metadata.labels.argocd.argoproj.io/secret-type": "cluster",
"metadata.labels.environment": "production",
"metadata.labels.org": "bar",
"name": "production_01/west",
"nameNormalized": "production-01-west",
"server": "https://production-01.example.com",
}},
},
{
name: "server filter with long url",
selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"server": "https://some-really-long-url-that-will-exceed-63-characters.com"},
},
expected: []map[string]interface{}{{
"metadata.annotations.foo.argoproj.io": "production",
"metadata.labels.argocd.argoproj.io/secret-type": "cluster",
"metadata.labels.environment": "production",
"metadata.labels.org": "bar",
"name": "some-really-long-server-url",
"nameNormalized": "some-really-long-server-url",
"server": "https://some-really-long-url-that-will-exceed-63-characters.com",
}},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
testGenerators := map[string]Generator{
"Clusters": getMockClusterGenerator(),
}
applicationSetInfo := argov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "set",
},
Spec: argov1alpha1.ApplicationSetSpec{},
}
results, err := Transform(
argov1alpha1.ApplicationSetGenerator{
Selector: testCase.selector,
Clusters: &argov1alpha1.ClusterGenerator{
Selector: metav1.LabelSelector{},
Template: argov1alpha1.ApplicationSetTemplate{},
Values: nil,
}},
testGenerators,
emptyTemplate(),
&applicationSetInfo, nil)
assert.NoError(t, err)
assert.ElementsMatch(t, testCase.expected, results[0].Params)
})
}
}
func emptyTemplate() argov1alpha1.ApplicationSetTemplate {
return argov1alpha1.ApplicationSetTemplate{
func emptyTemplate() argoprojiov1alpha1.ApplicationSetTemplate {
return argoprojiov1alpha1.ApplicationSetTemplate{
Spec: argov1alpha1.ApplicationSpec{
Project: "project",
},
@@ -305,35 +150,8 @@ func getMockClusterGenerator() Generator {
},
Type: corev1.SecretType("Opaque"),
},
&corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "some-really-long-server-url",
Namespace: "namespace",
Labels: map[string]string{
"argocd.argoproj.io/secret-type": "cluster",
"environment": "production",
"org": "bar",
},
Annotations: map[string]string{
"foo.argoproj.io": "production",
},
},
Data: map[string][]byte{
"config": []byte("{}"),
"name": []byte("some-really-long-server-url"),
"server": []byte("https://some-really-long-url-that-will-exceed-63-characters.com"),
},
Type: corev1.SecretType("Opaque"),
},
}
runtimeClusters := []runtime.Object{}
for _, clientCluster := range clusters {
runtimeClusters = append(runtimeClusters, clientCluster)
}
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
@@ -341,8 +159,8 @@ func getMockClusterGenerator() Generator {
}
func getMockGitGenerator() Generator {
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)
argoCDServiceMock := 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
}
@@ -430,60 +248,6 @@ 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 len(appSetGenerator.Git.Directories) != 0 {
if appSetGenerator.Git.Directories != nil {
res, err = g.generateParamsForGitDirectories(appSetGenerator, appSet.Spec.GoTemplate)
} else if len(appSetGenerator.Git.Files) != 0 {
} else if appSetGenerator.Git.Files != nil {
res, err = g.generateParamsForGitFiles(appSetGenerator, appSet.Spec.GoTemplate)
} else {
return nil, EmptyAppSetGeneratorError
@@ -81,10 +81,10 @@ func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoproj
}
log.WithFields(log.Fields{
"allPaths": allPaths,
"total": len(allPaths),
"repoURL": appSetGenerator.Git.RepoURL,
"revision": appSetGenerator.Git.Revision,
"allPaths": allPaths,
"total": len(allPaths),
"repoURL": appSetGenerator.Git.RepoURL,
"revision": appSetGenerator.Git.Revision,
"pathParamPrefix": appSetGenerator.Git.PathParamPrefix,
}).Info("applications result from the repo service")
@@ -127,7 +127,9 @@ func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1al
return nil, fmt.Errorf("unable to process file '%s': %v", path, err)
}
res = append(res, paramsArray...)
for index := range paramsArray {
res = append(res, paramsArray[index])
}
}
return res, nil
}
@@ -181,7 +183,7 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []
}
pathParamName := "path"
if pathParamPrefix != "" {
pathParamName = pathParamPrefix + "." + pathParamName
pathParamName = pathParamPrefix+"."+pathParamName
}
params[pathParamName] = path.Dir(filePath)
params[pathParamName+".basename"] = path.Base(params[pathParamName].(string))
@@ -249,7 +251,7 @@ func (g *GitGenerator) generateParamsFromApps(requestedApps []string, appSetGene
} else {
pathParamName := "path"
if appSetGenerator.Git.PathParamPrefix != "" {
pathParamName = appSetGenerator.Git.PathParamPrefix + "." + pathParamName
pathParamName = appSetGenerator.Git.PathParamPrefix+"."+pathParamName
}
params[pathParamName] = a
params[pathParamName+".basename"] = path.Base(a)

View File

@@ -1,6 +1,7 @@
package generators
import (
"context"
"fmt"
"testing"
@@ -8,7 +9,6 @@ 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,6 +20,33 @@ 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:
@@ -244,9 +271,9 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
t.Run(testCaseCopy.name, func(t *testing.T) {
t.Parallel()
argoCDServiceMock := testutils.ArgoCDServiceMock{Mock: &mock.Mock{}}
argoCDServiceMock := 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{
@@ -274,7 +301,7 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
assert.Equal(t, testCaseCopy.expected, got)
}
argoCDServiceMock.Mock.AssertExpectations(t)
argoCDServiceMock.mock.AssertExpectations(t)
})
}
}
@@ -539,9 +566,9 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) {
t.Run(testCaseCopy.name, func(t *testing.T) {
t.Parallel()
argoCDServiceMock := testutils.ArgoCDServiceMock{Mock: &mock.Mock{}}
argoCDServiceMock := 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{
@@ -570,7 +597,7 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) {
assert.Equal(t, testCaseCopy.expected, got)
}
argoCDServiceMock.Mock.AssertExpectations(t)
argoCDServiceMock.mock.AssertExpectations(t)
})
}
@@ -830,8 +857,8 @@ cluster:
t.Run(testCaseCopy.name, func(t *testing.T) {
t.Parallel()
argoCDServiceMock := testutils.ArgoCDServiceMock{Mock: &mock.Mock{}}
argoCDServiceMock.Mock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
argoCDServiceMock := 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)
@@ -860,7 +887,7 @@ cluster:
assert.ElementsMatch(t, testCaseCopy.expected, got)
}
argoCDServiceMock.Mock.AssertExpectations(t)
argoCDServiceMock.mock.AssertExpectations(t)
})
}
}
@@ -1179,8 +1206,8 @@ cluster:
t.Run(testCaseCopy.name, func(t *testing.T) {
t.Parallel()
argoCDServiceMock := testutils.ArgoCDServiceMock{Mock: &mock.Mock{}}
argoCDServiceMock.Mock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
argoCDServiceMock := 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)
@@ -1210,7 +1237,7 @@ cluster:
assert.ElementsMatch(t, testCaseCopy.expected, got)
}
argoCDServiceMock.Mock.AssertExpectations(t)
argoCDServiceMock.mock.AssertExpectations(t)
})
}
}

View File

@@ -6,7 +6,6 @@ import (
"time"
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"sigs.k8s.io/yaml"
)
var _ Generator = (*ListGenerator)(nil)
@@ -74,16 +73,5 @@ func (g *ListGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appli
}
}
// Append elements from ElementsYaml to the response
if len(appSetGenerator.List.ElementsYaml) > 0 {
var yamlElements []map[string]interface{}
err := yaml.Unmarshal([]byte(appSetGenerator.List.ElementsYaml), &yamlElements)
if err != nil {
return nil, fmt.Errorf("error unmarshling decoded ElementsYaml %v", err)
}
res = append(res, yamlElements...)
}
return res, nil
}

View File

@@ -5,7 +5,6 @@ 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"
@@ -17,7 +16,6 @@ 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"
)
@@ -30,7 +28,7 @@ func TestMatrixGenerate(t *testing.T) {
}
listGenerator := &argoprojiov1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "Cluster","url": "Url", "templated": "test-{{path.basenameNormalized}}"}`)}},
Elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "Cluster","url": "Url"}`)}},
}
testCases := []struct {
@@ -50,8 +48,8 @@ func TestMatrixGenerate(t *testing.T) {
},
},
expected: []map[string]interface{}{
{"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"},
{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "cluster": "Cluster", "url": "Url"},
{"path": "app2", "path.basename": "app2", "path.basenameNormalized": "app2", "cluster": "Cluster", "url": "Url"},
},
},
{
@@ -837,174 +835,6 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
}
}
func TestMatrixGenerateListElementsYaml(t *testing.T) {
gitGenerator := &argoprojiov1alpha1.GitGenerator{
RepoURL: "RepoURL",
Revision: "Revision",
Files: []argoprojiov1alpha1.GitFileGeneratorItem{
{Path: "config.yaml"},
},
}
listGenerator := &argoprojiov1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{},
ElementsYaml: "{{ .foo.bar | toJson }}",
}
testCases := []struct {
name string
baseGenerators []argoprojiov1alpha1.ApplicationSetNestedGenerator
expectedErr error
expected []map[string]interface{}
}{
{
name: "happy flow - generate params",
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
{
Git: gitGenerator,
},
{
List: listGenerator,
},
},
expected: []map[string]interface{}{
{
"chart": "a",
"version": "1",
"foo": map[string]interface{}{
"bar": []interface{}{
map[string]interface{}{
"chart": "a",
"version": "1",
},
map[string]interface{}{
"chart": "b",
"version": "2",
},
},
},
"path": map[string]interface{}{
"basename": "dir",
"basenameNormalized": "dir",
"filename": "file_name.yaml",
"filenameNormalized": "file-name.yaml",
"path": "path/dir",
"segments": []string {
"path",
"dir",
},
},
},
{
"chart": "b",
"version": "2",
"foo": map[string]interface{}{
"bar": []interface{}{
map[string]interface{}{
"chart": "a",
"version": "1",
},
map[string]interface{}{
"chart": "b",
"version": "2",
},
},
},
"path": map[string]interface{}{
"basename": "dir",
"basenameNormalized": "dir",
"filename": "file_name.yaml",
"filenameNormalized": "file-name.yaml",
"path": "path/dir",
"segments": []string {
"path",
"dir",
},
},
},
},
},
}
for _, testCase := range testCases {
testCaseCopy := testCase // Since tests may run in parallel
t.Run(testCaseCopy.name, func(t *testing.T) {
genMock := &generatorMock{}
appSet := &argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "set",
},
Spec: argoprojiov1alpha1.ApplicationSetSpec{
GoTemplate: true,
},
}
for _, g := range testCaseCopy.baseGenerators {
gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{
Git: g.Git,
List: g.List,
}
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet).Return([]map[string]any{{
"foo": map[string]interface{}{
"bar": []interface{}{
map[string]interface{}{
"chart": "a",
"version": "1",
},
map[string]interface{}{
"chart": "b",
"version": "2",
},
},
},
"path": map[string]interface{}{
"basename": "dir",
"basenameNormalized": "dir",
"filename": "file_name.yaml",
"filenameNormalized": "file-name.yaml",
"path": "path/dir",
"segments": []string {
"path",
"dir",
},
},
}}, nil)
genMock.On("GetTemplate", &gitGeneratorSpec).
Return(&argoprojiov1alpha1.ApplicationSetTemplate{})
}
var matrixGenerator = NewMatrixGenerator(
map[string]Generator{
"Git": genMock,
"List": &ListGenerator{},
},
)
got, err := matrixGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{
Matrix: &argoprojiov1alpha1.MatrixGenerator{
Generators: testCaseCopy.baseGenerators,
Template: argoprojiov1alpha1.ApplicationSetTemplate{},
},
}, appSet)
if testCaseCopy.expectedErr != nil {
assert.ErrorIs(t, err, testCaseCopy.expectedErr)
} else {
assert.NoError(t, err)
assert.Equal(t, testCaseCopy.expected, got)
}
})
}
}
type generatorMock struct {
mock.Mock
}
@@ -1027,72 +857,3 @@ 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

@@ -158,7 +158,7 @@ func TestListPullRequestBasicAuth(t *testing.T) {
func TestListResponseError(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
w.WriteHeader(500)
}))
defer ts.Close()
svc, _ := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO")

View File

@@ -29,7 +29,7 @@ func (c *ExtendedClient) GetContents(repo *Repository, path string) (bool, error
urlStr += fmt.Sprintf("/repositories/%s/%s/src/%s/%s?format=meta", c.owner, repo.Repository, repo.SHA, path)
body := strings.NewReader("")
req, err := http.NewRequest(http.MethodGet, urlStr, body)
req, err := http.NewRequest("GET", urlStr, body)
if err != nil {
return false, err
}

View File

@@ -3,7 +3,6 @@ package scm_provider
import (
"context"
"fmt"
"io"
"github.com/argoproj/argo-cd/v2/applicationset/utils"
bitbucketv1 "github.com/gfleury/go-bitbucket-v1"
@@ -184,9 +183,8 @@ func (b *BitbucketServerProvider) listBranches(repo *Repository) ([]bitbucketv1.
func (b *BitbucketServerProvider) getDefaultBranch(org string, repo string) (*bitbucketv1.Branch, error) {
response, err := b.client.DefaultApi.GetDefaultBranch(org, repo)
// The API will return 404 if a default branch is set but doesn't exist. In case the repo is empty and default branch is unset,
// we will get an EOF and a nil response.
if (response != nil && response.StatusCode == 404) || (response == nil && err == io.EOF) {
if response != nil && response.StatusCode == 404 {
// There's no default branch i.e. empty repo, not an error
return nil, nil
}
if err != nil {

View File

@@ -347,7 +347,7 @@ func TestGetBranchesMissingDefault(t *testing.T) {
assert.Empty(t, r.Header.Get("Authorization"))
switch r.RequestURI {
case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches/default":
http.Error(w, "Not found", http.StatusNotFound)
http.Error(w, "Not found", 404)
}
defaultHandler(t)(w, r)
}))
@@ -365,34 +365,12 @@ func TestGetBranchesMissingDefault(t *testing.T) {
assert.Empty(t, repos)
}
func TestGetBranchesEmptyRepo(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Empty(t, r.Header.Get("Authorization"))
switch r.RequestURI {
case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches/default":
return
}
}))
defer ts.Close()
provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false)
assert.NoError(t, err)
repos, err := provider.GetBranches(context.Background(), &Repository{
Organization: "PROJECT",
Repository: "REPO",
URL: "ssh://git@mycompany.bitbucket.org/PROJECT/REPO.git",
Labels: []string{},
RepositoryId: 1,
})
assert.Empty(t, repos)
assert.NoError(t, err)
}
func TestGetBranchesErrorDefaultBranch(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Empty(t, r.Header.Get("Authorization"))
switch r.RequestURI {
case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches/default":
http.Error(w, "Internal server error", http.StatusInternalServerError)
http.Error(w, "Internal server error", 500)
}
defaultHandler(t)(w, r)
}))
@@ -464,7 +442,7 @@ func TestListReposMissingDefaultBranch(t *testing.T) {
assert.Empty(t, r.Header.Get("Authorization"))
switch r.RequestURI {
case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches/default":
http.Error(w, "Not found", http.StatusNotFound)
http.Error(w, "Not found", 404)
}
defaultHandler(t)(w, r)
}))
@@ -481,7 +459,7 @@ func TestListReposErrorDefaultBranch(t *testing.T) {
assert.Empty(t, r.Header.Get("Authorization"))
switch r.RequestURI {
case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches/default":
http.Error(w, "Internal server error", http.StatusInternalServerError)
http.Error(w, "Internal server error", 500)
}
defaultHandler(t)(w, r)
}))
@@ -538,17 +516,17 @@ func TestBitbucketServerHasPath(t *testing.T) {
_, err = io.WriteString(w, `{"type":"FILE"}`)
case "/rest/api/1.0/projects/PROJECT/repos/REPO/browse/anotherpkg/missing.txt?at=main&limit=100&type=true":
http.Error(w, "The path \"anotherpkg/missing.txt\" does not exist at revision \"main\"", http.StatusNotFound)
http.Error(w, "The path \"anotherpkg/missing.txt\" does not exist at revision \"main\"", 404)
case "/rest/api/1.0/projects/PROJECT/repos/REPO/browse/notathing?at=main&limit=100&type=true":
http.Error(w, "The path \"notathing\" does not exist at revision \"main\"", http.StatusNotFound)
http.Error(w, "The path \"notathing\" does not exist at revision \"main\"", 404)
case "/rest/api/1.0/projects/PROJECT/repos/REPO/browse/return-redirect?at=main&limit=100&type=true":
http.Redirect(w, r, "http://"+r.Host+"/rest/api/1.0/projects/PROJECT/repos/REPO/browse/redirected?at=main&limit=100&type=true", http.StatusMovedPermanently)
http.Redirect(w, r, "http://"+r.Host+"/rest/api/1.0/projects/PROJECT/repos/REPO/browse/redirected?at=main&limit=100&type=true", 301)
case "/rest/api/1.0/projects/PROJECT/repos/REPO/browse/redirected?at=main&limit=100&type=true":
_, err = io.WriteString(w, `{"type":"DIRECTORY"}`)
case "/rest/api/1.0/projects/PROJECT/repos/REPO/browse/unauthorized-response?at=main&limit=100&type=true":
http.Error(w, "Authentication failed", http.StatusUnauthorized)
http.Error(w, "Authentication failed", 401)
default:
t.Fail()

View File

@@ -47,7 +47,7 @@ func NewGiteaProvider(ctx context.Context, owner, token, url string, allBranches
func (g *GiteaProvider) GetBranches(ctx context.Context, repo *Repository) ([]*Repository, error) {
if !g.allBranches {
branch, status, err := g.client.GetRepoBranch(g.owner, repo.Repository, repo.Branch)
if status.StatusCode == http.StatusNotFound {
if status.StatusCode == 404 {
return nil, fmt.Errorf("got 404 while getting default branch %q for repo %q - check your repo config: %w", repo.Branch, repo.Repository, err)
}
if err != nil {

View File

@@ -247,7 +247,7 @@ func giteaMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) {
_, err := io.WriteString(w, testdata.ReposGiteaGoSdkContentsGiteaResponse)
require.NoError(t, err)
case "/api/v1/repos/gitea/go-sdk/contents/notathing?ref=master":
w.WriteHeader(http.StatusNotFound)
w.WriteHeader(404)
_, err := io.WriteString(w, `{"errors":["object does not exist [id: , rel_path: notathing]"],"message":"GetContentsOrList","url":"https://gitea.com/api/swagger"}`)
require.NoError(t, err)
default:

View File

@@ -4,7 +4,6 @@ import (
"context"
"errors"
"fmt"
"net/http"
"os"
"github.com/google/go-github/v35/github"
@@ -124,7 +123,7 @@ func (g *GithubProvider) listBranches(ctx context.Context, repo *Repository) ([]
if err != nil {
var githubErrorResponse *github.ErrorResponse
if errors.As(err, &githubErrorResponse) {
if githubErrorResponse.Response.StatusCode == http.StatusNotFound {
if githubErrorResponse.Response.StatusCode == 404 {
// Default branch doesn't exist, so the repo is empty.
return []github.Branch{}, nil
}

View File

@@ -196,7 +196,7 @@ func githubMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) {
t.Fail()
}
default:
w.WriteHeader(http.StatusNotFound)
w.WriteHeader(404)
}
}
}

View File

@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"os"
"net/http"
pathpkg "path"
gitlab "github.com/xanzy/go-gitlab"
@@ -145,11 +144,7 @@ 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, 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
}
gitlabBranch, _, err := g.client.Branches.GetBranch(repo.RepositoryId, repo.Branch, nil)
if err != nil {
return nil, err
}
@@ -162,10 +157,6 @@ 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,8 +274,6 @@ 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 {
@@ -393,29 +391,3 @@ 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

@@ -1,261 +0,0 @@
package utils
import (
"fmt"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/klog/v2"
"sort"
"strconv"
"strings"
)
var (
unaryOperators = []string{
string(selection.Exists), string(selection.DoesNotExist),
}
binaryOperators = []string{
string(selection.In), string(selection.NotIn),
string(selection.Equals), string(selection.DoubleEquals), string(selection.NotEquals),
string(selection.GreaterThan), string(selection.LessThan),
}
validRequirementOperators = append(binaryOperators, unaryOperators...)
)
// Selector represents a label selector.
type Selector interface {
// Matches returns true if this selector matches the given set of labels.
Matches(labels.Labels) bool
// Add adds requirements to the Selector
Add(r ...Requirement) Selector
}
type internalSelector []Requirement
// ByKey sorts requirements by key to obtain deterministic parser
type ByKey []Requirement
func (a ByKey) Len() int { return len(a) }
func (a ByKey) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByKey) Less(i, j int) bool { return a[i].key < a[j].key }
// Matches for a internalSelector returns true if all
// its Requirements match the input Labels. If any
// Requirement does not match, false is returned.
func (s internalSelector) Matches(l labels.Labels) bool {
for ix := range s {
if matches := s[ix].Matches(l); !matches {
return false
}
}
return true
}
// Add adds requirements to the selector. It copies the current selector returning a new one
func (s internalSelector) Add(reqs ...Requirement) Selector {
ret := make(internalSelector, 0, len(s)+len(reqs))
ret = append(ret, s...)
ret = append(ret, reqs...)
sort.Sort(ByKey(ret))
return ret
}
type nothingSelector struct{}
func (n nothingSelector) Matches(l labels.Labels) bool {
return false
}
func (n nothingSelector) Add(r ...Requirement) Selector {
return n
}
// Nothing returns a selector that matches no labels
func nothing() Selector {
return nothingSelector{}
}
// Everything returns a selector that matches all labels.
func everything() Selector {
return internalSelector{}
}
// LabelSelectorAsSelector converts the LabelSelector api type into a struct that implements
// labels.Selector
// Note: This function should be kept in sync with the selector methods in pkg/labels/selector.go
func LabelSelectorAsSelector(ps *v1.LabelSelector) (Selector, error) {
if ps == nil {
return nothing(), nil
}
if len(ps.MatchLabels)+len(ps.MatchExpressions) == 0 {
return everything(), nil
}
requirements := make([]Requirement, 0, len(ps.MatchLabels)+len(ps.MatchExpressions))
for k, v := range ps.MatchLabels {
r, err := newRequirement(k, selection.Equals, []string{v})
if err != nil {
return nil, err
}
requirements = append(requirements, *r)
}
for _, expr := range ps.MatchExpressions {
var op selection.Operator
switch expr.Operator {
case v1.LabelSelectorOpIn:
op = selection.In
case v1.LabelSelectorOpNotIn:
op = selection.NotIn
case v1.LabelSelectorOpExists:
op = selection.Exists
case v1.LabelSelectorOpDoesNotExist:
op = selection.DoesNotExist
default:
return nil, fmt.Errorf("%q is not a valid pod selector operator", expr.Operator)
}
r, err := newRequirement(expr.Key, op, append([]string(nil), expr.Values...))
if err != nil {
return nil, err
}
requirements = append(requirements, *r)
}
selector := newSelector()
selector = selector.Add(requirements...)
return selector, nil
}
// NewSelector returns a nil selector
func newSelector() Selector {
return internalSelector(nil)
}
func validateLabelKey(k string, path *field.Path) *field.Error {
if errs := validation.IsQualifiedName(k); len(errs) != 0 {
return field.Invalid(path, k, strings.Join(errs, "; "))
}
return nil
}
// NewRequirement is the constructor for a Requirement.
// If any of these rules is violated, an error is returned:
// (1) The operator can only be In, NotIn, Equals, DoubleEquals, Gt, Lt, NotEquals, Exists, or DoesNotExist.
// (2) If the operator is In or NotIn, the values set must be non-empty.
// (3) If the operator is Equals, DoubleEquals, or NotEquals, the values set must contain one value.
// (4) If the operator is Exists or DoesNotExist, the value set must be empty.
// (5) If the operator is Gt or Lt, the values set must contain only one value, which will be interpreted as an integer.
// (6) The key is invalid due to its length, or sequence
//
// of characters. See validateLabelKey for more details.
//
// The empty string is a valid value in the input values set.
// Returned error, if not nil, is guaranteed to be an aggregated field.ErrorList
func newRequirement(key string, op selection.Operator, vals []string, opts ...field.PathOption) (*Requirement, error) {
var allErrs field.ErrorList
path := field.ToPath(opts...)
if err := validateLabelKey(key, path.Child("key")); err != nil {
allErrs = append(allErrs, err)
}
valuePath := path.Child("values")
switch op {
case selection.In, selection.NotIn:
if len(vals) == 0 {
allErrs = append(allErrs, field.Invalid(valuePath, vals, "for 'in', 'notin' operators, values set can't be empty"))
}
case selection.Equals, selection.DoubleEquals, selection.NotEquals:
if len(vals) != 1 {
allErrs = append(allErrs, field.Invalid(valuePath, vals, "exact-match compatibility requires one single value"))
}
case selection.Exists, selection.DoesNotExist:
if len(vals) != 0 {
allErrs = append(allErrs, field.Invalid(valuePath, vals, "values set must be empty for exists and does not exist"))
}
case selection.GreaterThan, selection.LessThan:
if len(vals) != 1 {
allErrs = append(allErrs, field.Invalid(valuePath, vals, "for 'Gt', 'Lt' operators, exactly one value is required"))
}
for i := range vals {
if _, err := strconv.ParseInt(vals[i], 10, 64); err != nil {
allErrs = append(allErrs, field.Invalid(valuePath.Index(i), vals[i], "for 'Gt', 'Lt' operators, the value must be an integer"))
}
}
default:
allErrs = append(allErrs, field.NotSupported(path.Child("operator"), op, validRequirementOperators))
}
return &Requirement{key: key, operator: op, strValues: vals}, allErrs.ToAggregate()
}
// Requirement contains values, a key, and an operator that relates the key and values.
// The zero value of Requirement is invalid.
// Requirement implements both set based match and exact match
// Requirement should be initialized via NewRequirement constructor for creating a valid Requirement.
// +k8s:deepcopy-gen=true
type Requirement struct {
key string
operator selection.Operator
// In the majority of cases we have at most one value here.
// It is generally faster to operate on a single-element slice
// than on a single-element map, so we have a slice here.
strValues []string
}
func (r *Requirement) hasValue(value string) bool {
for i := range r.strValues {
if r.strValues[i] == value {
return true
}
}
return false
}
func (r *Requirement) Matches(ls labels.Labels) bool {
switch r.operator {
case selection.In, selection.Equals, selection.DoubleEquals:
if !ls.Has(r.key) {
return false
}
return r.hasValue(ls.Get(r.key))
case selection.NotIn, selection.NotEquals:
if !ls.Has(r.key) {
return true
}
return !r.hasValue(ls.Get(r.key))
case selection.Exists:
return ls.Has(r.key)
case selection.DoesNotExist:
return !ls.Has(r.key)
case selection.GreaterThan, selection.LessThan:
if !ls.Has(r.key) {
return false
}
lsValue, err := strconv.ParseInt(ls.Get(r.key), 10, 64)
if err != nil {
klog.V(10).Infof("ParseInt failed for value %+v in label %+v, %+v", ls.Get(r.key), ls, err)
return false
}
// There should be only one strValue in r.strValues, and can be converted to an integer.
if len(r.strValues) != 1 {
klog.V(10).Infof("Invalid values count %+v of requirement %#v, for 'Gt', 'Lt' operators, exactly one value is required", len(r.strValues), r)
return false
}
var rValue int64
for i := range r.strValues {
rValue, err = strconv.ParseInt(r.strValues[i], 10, 64)
if err != nil {
klog.V(10).Infof("ParseInt failed for value %+v in requirement %#v, for 'Gt', 'Lt' operators, the value must be an integer", r.strValues[i], r)
return false
}
}
return (r.operator == selection.GreaterThan && lsValue > rValue) || (r.operator == selection.LessThan && lsValue < rValue)
default:
return false
}
}

View File

@@ -1,34 +0,0 @@
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,25 +96,6 @@ 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
}
@@ -193,7 +174,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 {
@@ -223,27 +204,6 @@ 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.
@@ -267,10 +227,7 @@ func (r *Render) Replace(tmpl string, replaceMap map[string]interface{}, useGoTe
return tmpl, nil
}
fstTmpl, err := fasttemplate.NewTemplate(tmpl, "{{", "}}")
if err != nil {
return "", fmt.Errorf("invalid template: %w", err)
}
fstTmpl := fasttemplate.New(tmpl, "{{", "}}")
replacedTmpl := fstTmpl.ExecuteFuncString(func(w io.Writer, tag string) (int, error) {
trimmedTag := strings.TrimSpace(tag)
replacement, ok := replaceMap[trimmedTag].(string)

View File

@@ -464,14 +464,6 @@ func TestRenderTemplateParamsGoTemplate(t *testing.T) {
}
}
func Test_Render_Replace_no_panic_on_missing_closing_brace(t *testing.T) {
r := &Render{}
assert.NotPanics(t, func() {
_, err := r.Replace("{{properly.closed}} {{improperly.closed}", nil, false)
assert.Error(t, err)
})
}
func TestRenderTemplateKeys(t *testing.T) {
t.Run("fasttemplate", func(t *testing.T) {
application := &argoappsv1.Application{

View File

@@ -133,7 +133,7 @@ func (h *WebhookHandler) Handler(w http.ResponseWriter, r *http.Request) {
if err != nil {
log.Infof("Webhook processing failed: %s", err)
status := http.StatusBadRequest
if r.Method != http.MethodPost {
if r.Method != "POST" {
status = http.StatusMethodNotAllowed
}
http.Error(w, fmt.Sprintf("Webhook processing failed: %s", html.EscapeString(err.Error())), status)

View File

@@ -177,7 +177,7 @@ func TestWebhookHandler(t *testing.T) {
h, err := NewWebhookHandler(namespace, set, fc, mockGenerators())
assert.Nil(t, err)
req := httptest.NewRequest(http.MethodPost, "/api/webhook", nil)
req := httptest.NewRequest("POST", "/api/webhook", nil)
req.Header.Set(test.headerKey, test.headerValue)
eventJSON, err := os.ReadFile(filepath.Join("testdata", test.payloadFile))
assert.NoError(t, err)

View File

@@ -271,16 +271,6 @@
"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": {
@@ -595,16 +585,6 @@
"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": {
@@ -3690,16 +3670,6 @@
"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": {
@@ -5705,17 +5675,6 @@
}
}
},
"v1alpha1ApplicationPreservedFields": {
"type": "object",
"properties": {
"annotations": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"v1alpha1ApplicationSet": {
"type": "object",
"title": "ApplicationSet is a set of Application resources\n+genclient\n+genclient:noStatus\n+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n+kubebuilder:resource:path=applicationsets,shortName=appset;appsets\n+kubebuilder:subresource:status",
@@ -5900,9 +5859,6 @@
"goTemplate": {
"type": "boolean"
},
"preservedFields": {
"$ref": "#/definitions/v1alpha1ApplicationPreservedFields"
},
"strategy": {
"$ref": "#/definitions/v1alpha1ApplicationSetStrategy"
},
@@ -6145,10 +6101,6 @@
"type": "string"
}
},
"commonAnnotationsEnvsubst": {
"type": "boolean",
"title": "CommonAnnotationsEnvsubst specifies whether to apply env variables substitution for annotation values"
},
"commonLabels": {
"type": "object",
"title": "CommonLabels is a list of additional labels to add to rendered manifests",
@@ -6179,17 +6131,6 @@
"type": "string",
"title": "NameSuffix is a suffix appended to resources for Kustomize apps"
},
"namespace": {
"type": "string",
"title": "Namespace sets the namespace that Kustomize adds to all resources"
},
"replicas": {
"type": "array",
"title": "Replicas is a list of Kustomize Replicas override specifications",
"items": {
"$ref": "#/definitions/v1alpha1KustomizeReplica"
}
},
"version": {
"type": "string",
"title": "Version controls which version of Kustomize to use for rendering manifests"
@@ -7022,18 +6963,6 @@
}
}
},
"v1alpha1KustomizeReplica": {
"type": "object",
"properties": {
"count": {
"$ref": "#/definitions/intstrIntOrString"
},
"name": {
"type": "string",
"title": "Name of Deployment or StatefulSet"
}
}
},
"v1alpha1ListGenerator": {
"type": "object",
"title": "ListGenerator include items info",
@@ -7044,9 +6973,6 @@
"$ref": "#/definitions/v1JSON"
}
},
"elementsYaml": {
"type": "string"
},
"template": {
"$ref": "#/definitions/v1alpha1ApplicationSetTemplate"
}

View File

@@ -7,7 +7,7 @@ import (
"time"
"github.com/argoproj/pkg/stats"
"github.com/redis/go-redis/v9"
"github.com/go-redis/redis/v8"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"k8s.io/client-go/kubernetes"
@@ -188,7 +188,7 @@ func NewCommand() *cobra.Command {
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDMetrics, "Start metrics server on given port")
command.Flags().DurationVar(&metricsCacheExpiration, "metrics-cache-expiration", env.ParseDurationFromEnv("ARGOCD_APPLICATION_CONTROLLER_METRICS_CACHE_EXPIRATION", 0*time.Second, 0, math.MaxInt64), "Prometheus metrics cache expiration (disabled by default. e.g. 24h0m0s)")
command.Flags().IntVar(&selfHealTimeoutSeconds, "self-heal-timeout-seconds", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_TIMEOUT_SECONDS", 5, 0, math.MaxInt32), "Specifies timeout between application self heal attempts")
command.Flags().Int64Var(&kubectlParallelismLimit, "kubectl-parallelism-limit", env.ParseInt64FromEnv("ARGOCD_APPLICATION_CONTROLLER_KUBECTL_PARALLELISM_LIMIT", 20, 0, math.MaxInt64), "Number of allowed concurrent kubectl fork/execs. Any value less than 1 means no limit.")
command.Flags().Int64Var(&kubectlParallelismLimit, "kubectl-parallelism-limit", 20, "Number of allowed concurrent kubectl fork/execs. Any value less the 1 means no limit.")
command.Flags().BoolVar(&repoServerPlaintext, "repo-server-plaintext", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_PLAINTEXT", false), "Disable TLS on connections to repo server")
command.Flags().BoolVar(&repoServerStrictTLS, "repo-server-strict-tls", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_STRICT_TLS", false), "Whether to use strict validation of the TLS cert presented by the repo server")
command.Flags().StringSliceVar(&metricsAplicationLabels, "metrics-application-labels", []string{}, "List of Application labels that will be added to the argocd_application_labels metric")

View File

@@ -30,6 +30,7 @@ import (
"k8s.io/client-go/tools/clientcmd"
"github.com/argoproj/argo-cd/v2/applicationset/services"
appsetv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
"github.com/argoproj/argo-cd/v2/util/cli"
@@ -59,6 +60,7 @@ func NewCommand() *cobra.Command {
)
scheme := runtime.NewScheme()
_ = clientgoscheme.AddToScheme(scheme)
_ = appsetv1alpha1.AddToScheme(scheme)
_ = appv1alpha1.AddToScheme(scheme)
var command = cobra.Command{
Use: "controller",
@@ -177,7 +179,7 @@ func NewCommand() *cobra.Command {
KubeClientset: k8sClient,
ArgoDB: argoCDDB,
EnableProgressiveSyncs: enableProgressiveSyncs,
}).SetupWithManager(mgr, enableProgressiveSyncs); err != nil {
}).SetupWithManager(mgr); err != nil {
log.Error(err, "unable to create controller", "controller", "ApplicationSet")
os.Exit(1)
}

View File

@@ -44,14 +44,6 @@ func NewCommand() *cobra.Command {
config, err := plugin.ReadPluginConfig(configFilePath)
errors.CheckError(err)
if !config.Spec.Discover.IsDefined() {
name := config.Metadata.Name
if config.Spec.Version != "" {
name = name + "-" + config.Spec.Version
}
log.Infof("No discovery configuration is defined for plugin %s. To use this plugin, specify %q in the Application's spec.source.plugin.name field.", config.Metadata.Name, name)
}
if otlpAddress != "" {
var closer func()
var err error

View File

@@ -55,7 +55,6 @@ func NewCommand() *cobra.Command {
argocdRepoServerStrictTLS bool
configMapName string
secretName string
applicationNamespaces []string
)
var command = cobra.Command{
Use: "controller",
@@ -139,7 +138,7 @@ func NewCommand() *cobra.Command {
log.Infof("serving metrics on port %d", metricsPort)
log.Infof("loading configuration %d", metricsPort)
ctrl := notificationscontroller.NewController(k8sClient, dynamicClient, argocdService, namespace, applicationNamespaces, appLabelSelector, registry, secretName, configMapName)
ctrl := notificationscontroller.NewController(k8sClient, dynamicClient, argocdService, namespace, appLabelSelector, registry, secretName, configMapName)
err = ctrl.Init(ctx)
if err != nil {
return err
@@ -157,11 +156,10 @@ func NewCommand() *cobra.Command {
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
command.Flags().StringVar(&logFormat, "logformat", "text", "Set the logging format. One of: text|json")
command.Flags().IntVar(&metricsPort, "metrics-port", defaultMetricsPort, "Metrics port")
command.Flags().StringVar(&argocdRepoServer, "argocd-repo-server", common.DefaultRepoServerAddr, "Argo CD repo server address")
command.Flags().StringVar(&argocdRepoServer, "argocd-repo-server", "argocd-repo-server:8081", "Argo CD repo server address")
command.Flags().BoolVar(&argocdRepoServerPlaintext, "argocd-repo-server-plaintext", false, "Use a plaintext client (non-TLS) to connect to repository server")
command.Flags().BoolVar(&argocdRepoServerStrictTLS, "argocd-repo-server-strict-tls", false, "Perform strict validation of TLS certificates when connecting to repo server")
command.Flags().StringVar(&configMapName, "config-map-name", "argocd-notifications-cm", "Set notifications ConfigMap name")
command.Flags().StringVar(&secretName, "secret-name", "argocd-notifications-secret", "Set notifications Secret name")
command.Flags().StringSliceVar(&applicationNamespaces, "application-namespaces", env.StringsFromEnv("ARGOCD_APPLICATION_NAMESPACES", []string{}, ","), "List of additional namespaces that this controller should send notifications for")
return &command
}

View File

@@ -9,7 +9,7 @@ import (
"time"
"github.com/argoproj/pkg/stats"
"github.com/redis/go-redis/v9"
"github.com/go-redis/redis/v8"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"google.golang.org/grpc/health/grpc_health_v1"
@@ -84,8 +84,6 @@ func NewCommand() *cobra.Command {
allowOutOfBoundsSymlinks bool
streamedManifestMaxTarSize string
streamedManifestMaxExtractedSize string
helmManifestMaxExtractedSize string
disableManifestMaxExtractedSize bool
)
var command = cobra.Command{
Use: cliName,
@@ -124,9 +122,6 @@ func NewCommand() *cobra.Command {
streamedManifestMaxExtractedSizeQuantity, err := resource.ParseQuantity(streamedManifestMaxExtractedSize)
errors.CheckError(err)
helmManifestMaxExtractedSizeQuantity, err := resource.ParseQuantity(helmManifestMaxExtractedSize)
errors.CheckError(err)
askPassServer := askpass.NewServer()
metricsServer := metrics.NewMetricsServer()
cacheutil.CollectMetrics(redisClient, metricsServer)
@@ -141,7 +136,6 @@ func NewCommand() *cobra.Command {
AllowOutOfBoundsSymlinks: allowOutOfBoundsSymlinks,
StreamedManifestMaxExtractedSize: streamedManifestMaxExtractedSizeQuantity.ToDec().Value(),
StreamedManifestMaxTarSize: streamedManifestMaxTarSizeQuantity.ToDec().Value(),
HelmManifestMaxExtractedSize: helmManifestMaxExtractedSizeQuantity.ToDec().Value(),
}, askPassServer)
errors.CheckError(err)
@@ -222,8 +216,6 @@ func NewCommand() *cobra.Command {
command.Flags().BoolVar(&allowOutOfBoundsSymlinks, "allow-oob-symlinks", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_ALLOW_OUT_OF_BOUNDS_SYMLINKS", false), "Allow out-of-bounds symlinks in repositories (not recommended)")
command.Flags().StringVar(&streamedManifestMaxTarSize, "streamed-manifest-max-tar-size", env.StringFromEnv("ARGOCD_REPO_SERVER_STREAMED_MANIFEST_MAX_TAR_SIZE", "100M"), "Maximum size of streamed manifest archives")
command.Flags().StringVar(&streamedManifestMaxExtractedSize, "streamed-manifest-max-extracted-size", env.StringFromEnv("ARGOCD_REPO_SERVER_STREAMED_MANIFEST_MAX_EXTRACTED_SIZE", "1G"), "Maximum size of streamed manifest archives when extracted")
command.Flags().StringVar(&helmManifestMaxExtractedSize, "helm-manifest-max-extracted-size", env.StringFromEnv("ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE", "1G"), "Maximum size of helm manifest archives when extracted")
command.Flags().BoolVar(&disableManifestMaxExtractedSize, "disable-helm-manifest-max-extracted-size", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE", false), "Disable maximum size of helm manifest archives when extracted")
tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command)
cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
redisClient = client

View File

@@ -4,11 +4,10 @@ import (
"context"
"fmt"
"math"
"strings"
"time"
"github.com/argoproj/pkg/stats"
"github.com/redis/go-redis/v9"
"github.com/go-redis/redis/v8"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"k8s.io/client-go/kubernetes"
@@ -61,7 +60,6 @@ func NewCommand() *cobra.Command {
repoServerAddress string
dexServerAddress string
disableAuth bool
contentTypes string
enableGZip bool
tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error)
cacheSrc func() (*servercache.Cache, error)
@@ -166,11 +164,6 @@ func NewCommand() *cobra.Command {
baseHRef = rootPath
}
var contentTypesList []string
if contentTypes != "" {
contentTypesList = strings.Split(contentTypes, ";")
}
argoCDOpts := server.ArgoCDServerOpts{
Insecure: insecure,
ListenPort: listenPort,
@@ -184,7 +177,6 @@ func NewCommand() *cobra.Command {
DexServerAddr: dexServerAddress,
DexTLSConfig: dexTlsConfig,
DisableAuth: disableAuth,
ContentTypes: contentTypesList,
EnableGZip: enableGZip,
TLSConfigCustomizer: tlsConfigCustomizer,
Cache: cache,
@@ -233,7 +225,6 @@ func NewCommand() *cobra.Command {
command.Flags().StringVar(&dexServerAddress, "dex-server", env.StringFromEnv("ARGOCD_SERVER_DEX_SERVER", common.DefaultDexServerAddr), "Dex server address")
command.Flags().BoolVar(&disableAuth, "disable-auth", env.ParseBoolFromEnv("ARGOCD_SERVER_DISABLE_AUTH", false), "Disable client authentication")
command.Flags().BoolVar(&enableGZip, "enable-gzip", env.ParseBoolFromEnv("ARGOCD_SERVER_ENABLE_GZIP", false), "Enable GZIP compression")
command.Flags().StringVar(&contentTypes, "api-content-types", env.StringFromEnv("ARGOCD_API_CONTENT_TYPES", "application/json"), "Semicolon separated list of allowed content types for non GET api requests. Any content type is allowed if empty.")
command.AddCommand(cli.NewVersionCmd(cliName))
command.Flags().IntVar(&listenPort, "port", common.DefaultPortAPIServer, "Listen on given port")
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDAPIServerMetrics, "Start metrics on given port")

View File

@@ -17,8 +17,6 @@ import (
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/settings"
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
)
const (
@@ -29,9 +27,9 @@ const (
var (
configMapResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}
secretResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}
applicationsResource = schema.GroupVersionResource{Group: application.Group, Version: "v1alpha1", Resource: application.ApplicationPlural}
appprojectsResource = schema.GroupVersionResource{Group: application.Group, Version: "v1alpha1", Resource: application.AppProjectPlural}
appplicationSetResource = schema.GroupVersionResource{Group: application.Group, Version: "v1alpha1", Resource: application.ApplicationSetPlural}
applicationsResource = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "applications"}
appprojectsResource = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "appprojects"}
appplicationSetResource = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "applicationsets"}
)
// NewAdminCommand returns a new instance of an argocd command
@@ -195,11 +193,11 @@ func specsEqual(left, right unstructured.Unstructured) bool {
leftData, _, _ := unstructured.NestedMap(left.Object, "data")
rightData, _, _ := unstructured.NestedMap(right.Object, "data")
return reflect.DeepEqual(leftData, rightData)
case application.AppProjectKind:
case "AppProject":
leftSpec, _, _ := unstructured.NestedMap(left.Object, "spec")
rightSpec, _, _ := unstructured.NestedMap(right.Object, "spec")
return reflect.DeepEqual(leftSpec, rightSpec)
case application.ApplicationKind:
case "Application":
leftSpec, _, _ := unstructured.NestedMap(left.Object, "spec")
rightSpec, _, _ := unstructured.NestedMap(right.Object, "spec")
leftStatus, _, _ := unstructured.NestedMap(left.Object, "status")

View File

@@ -17,7 +17,6 @@ import (
"k8s.io/client-go/tools/clientcmd"
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/errors"
)
@@ -177,12 +176,12 @@ func NewImportCommand() *cobra.Command {
applications, err := acdClients.applications.List(ctx, v1.ListOptions{})
errors.CheckError(err)
for _, app := range applications.Items {
pruneObjects[kube.ResourceKey{Group: application.Group, Kind: application.ApplicationKind, Name: app.GetName()}] = app
pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "Application", Name: app.GetName()}] = app
}
projects, err := acdClients.projects.List(ctx, v1.ListOptions{})
errors.CheckError(err)
for _, proj := range projects.Items {
pruneObjects[kube.ResourceKey{Group: application.Group, Kind: application.AppProjectKind, Name: proj.GetName()}] = proj
pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "AppProject", Name: proj.GetName()}] = proj
}
applicationSets, err := acdClients.applicationSets.List(ctx, v1.ListOptions{})
if apierr.IsForbidden(err) || apierr.IsNotFound(err) {
@@ -192,7 +191,7 @@ func NewImportCommand() *cobra.Command {
}
if applicationSets != nil {
for _, appSet := range applicationSets.Items {
pruneObjects[kube.ResourceKey{Group: application.Group, Kind: application.ApplicationSetKind, Name: appSet.GetName()}] = appSet
pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "ApplicationSet", Name: appSet.GetName()}] = appSet
}
}
@@ -210,11 +209,11 @@ func NewImportCommand() *cobra.Command {
dynClient = acdClients.secrets
case "ConfigMap":
dynClient = acdClients.configMaps
case application.AppProjectKind:
case "AppProject":
dynClient = acdClients.projects
case application.ApplicationKind:
case "Application":
dynClient = acdClients.applications
case application.ApplicationSetKind:
case "ApplicationSet":
dynClient = acdClients.applicationSets
}
if !exists {
@@ -261,9 +260,9 @@ func NewImportCommand() *cobra.Command {
switch key.Kind {
case "Secret":
dynClient = acdClients.secrets
case application.AppProjectKind:
case "AppProject":
dynClient = acdClients.projects
case application.ApplicationKind:
case "Application":
dynClient = acdClients.applications
if !dryRun {
if finalizers := liveObj.GetFinalizers(); len(finalizers) > 0 {
@@ -275,7 +274,7 @@ func NewImportCommand() *cobra.Command {
}
}
}
case application.ApplicationSetKind:
case "ApplicationSet":
dynClient = acdClients.applicationSets
default:
log.Fatalf("Unexpected kind '%s' in prune list", key.Kind)
@@ -315,7 +314,7 @@ func checkAppHasNoNeedToStopOperation(liveObj unstructured.Unstructured, stopOpe
return true
}
switch liveObj.GetKind() {
case application.ApplicationKind:
case "Application":
return liveObj.Object["operation"] == nil
}
return true
@@ -354,9 +353,9 @@ func updateLive(bak, live *unstructured.Unstructured, stopOperation bool) *unstr
switch live.GetKind() {
case "Secret", "ConfigMap":
newLive.Object["data"] = bak.Object["data"]
case application.AppProjectKind:
case "AppProject":
newLive.Object["spec"] = bak.Object["spec"]
case application.ApplicationKind:
case "Application":
newLive.Object["spec"] = bak.Object["spec"]
if _, ok := bak.Object["status"]; ok {
newLive.Object["status"] = bak.Object["status"]

View File

@@ -11,7 +11,7 @@ import (
"time"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/redis/go-redis/v9"
"github.com/go-redis/redis/v8"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -265,7 +265,9 @@ func runClusterNamespacesCommand(ctx context.Context, clientConfig clientcmd.Cli
}
}
} else {
nsSet[app.Spec.Destination.Namespace] = true
if app.Spec.Destination.Server == cluster.Server {
nsSet[app.Spec.Destination.Namespace] = true
}
}
}
var namespaces []string
@@ -541,11 +543,6 @@ func NewGenClusterConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command
return
}
if clusterOpts.InCluster && clusterOpts.ClusterEndpoint != "" {
log.Fatal("Can only use one of --in-cluster or --cluster-endpoint")
return
}
overrides := clientcmd.ConfigOverrides{
Context: *clstContext,
}
@@ -585,13 +582,9 @@ func NewGenClusterConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command
errors.CheckError(err)
clst := cmdutil.NewCluster(contextName, clusterOpts.Namespaces, clusterOpts.ClusterResources, conf, bearerToken, awsAuthConf, execProviderConf, labelsMap, annotationsMap)
if clusterOpts.InClusterEndpoint() {
if clusterOpts.InCluster {
clst.Server = argoappv1.KubernetesInternalAPIServerAddr
}
if clusterOpts.ClusterEndpoint == string(cmdutil.KubePublicEndpoint) {
// Ignore `kube-public` cluster endpoints, since this command is intended to run without invoking any network connections.
log.Warn("kube-public cluster endpoints are not supported. Falling back to the endpoint listed in the kubconfig context.")
}
if clusterOpts.Shard >= 0 {
clst.Shard = &clusterOpts.Shard
}

View File

@@ -9,16 +9,13 @@ 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
compressionStr string
port int
address string
)
cmd := &cobra.Command{
Use: "dashboard",
@@ -26,9 +23,7 @@ func NewDashboardCommand() *cobra.Command {
Run: func(cmd *cobra.Command, args []string) {
ctx := cmd.Context()
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))
errors.CheckError(headless.StartLocalServer(ctx, &argocdclient.ClientOptions{Core: true}, initialize.RetrieveContextIfChanged(cmd.Flag("context")), &port, &address))
println(fmt.Sprintf("Argo CD UI is available at http://%s:%d", address, port))
<-ctx.Done()
},
@@ -36,6 +31,5 @@ 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

@@ -15,13 +15,12 @@ import (
settings "github.com/argoproj/argo-cd/v2/util/notification/settings"
"github.com/argoproj/argo-cd/v2/util/tls"
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
"github.com/argoproj/notifications-engine/pkg/cmd"
"github.com/spf13/cobra"
)
var (
applications = schema.GroupVersionResource{Group: application.Group, Version: "v1alpha1", Resource: application.ApplicationPlural}
applications = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "applications"}
)
func NewNotificationsCommand() *cobra.Command {
@@ -65,7 +64,7 @@ func NewNotificationsCommand() *cobra.Command {
log.Fatalf("Failed to initialize Argo CD service: %v", err)
}
})
toolsCommand.PersistentFlags().StringVar(&argocdRepoServer, "argocd-repo-server", common.DefaultRepoServerAddr, "Argo CD repo server address")
toolsCommand.PersistentFlags().StringVar(&argocdRepoServer, "argocd-repo-server", "argocd-repo-server:8081", "Argo CD repo server address")
toolsCommand.PersistentFlags().BoolVar(&argocdRepoServerPlaintext, "argocd-repo-server-plaintext", false, "Use a plaintext client (non-TLS) to connect to repository server")
toolsCommand.PersistentFlags().BoolVar(&argocdRepoServerStrictTLS, "argocd-repo-server-strict-tls", false, "Perform strict validation of TLS certificates when connecting to repo server")
return toolsCommand

View File

@@ -28,8 +28,6 @@ import (
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
// load the azure plugin (required to authenticate with AKS clusters).
_ "k8s.io/client-go/plugin/pkg/client/auth/azure"
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
)
// NewProjectAllowListGenCommand generates a project from clusterRole
@@ -153,7 +151,7 @@ func generateProjectAllowList(serverResources []*metav1.APIResourceList, cluster
}
globalProj := v1alpha1.AppProject{
TypeMeta: metav1.TypeMeta{
Kind: application.AppProjectKind,
Kind: "AppProject",
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{Name: projName},

View File

@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"os"
"strings"
"github.com/ghodss/yaml"
log "github.com/sirupsen/logrus"
@@ -374,9 +373,6 @@ func resolveRBACResourceName(name string) string {
// isValidRBACAction checks whether a given action is a valid RBAC action
func isValidRBACAction(action string) bool {
if strings.HasPrefix(action, rbacpolicy.ActionAction+"/") {
return true
}
_, ok := validRBACActions[action]
return ok
}

View File

@@ -27,11 +27,6 @@ func Test_isValidRBACAction(t *testing.T) {
})
}
func Test_isValidRBACAction_ActionAction(t *testing.T) {
ok := isValidRBACAction("action/apps/Deployment/restart")
assert.True(t, ok)
}
func Test_isValidRBACResource(t *testing.T) {
for k := range validRBACResources {
t.Run(k, func(t *testing.T) {

View File

@@ -165,9 +165,7 @@ 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})
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 {
if grpc.UnwrapGRPCStatus(err).Code() != codes.NotFound {
errors.CheckError(err)
}
@@ -645,9 +643,7 @@ type unsetOpts struct {
namePrefix bool
nameSuffix bool
kustomizeVersion bool
kustomizeNamespace bool
kustomizeImages []string
kustomizeReplicas []string
parameters []string
valuesFiles []string
valuesLiteral bool
@@ -656,17 +652,6 @@ type unsetOpts struct {
passCredentials bool
}
// IsZero returns true when the Application options for kustomize are considered empty
func (o *unsetOpts) KustomizeIsZero() bool {
return o == nil ||
!o.namePrefix &&
!o.nameSuffix &&
!o.kustomizeVersion &&
!o.kustomizeNamespace &&
len(o.kustomizeImages) == 0 &&
len(o.kustomizeReplicas) == 0
}
// NewApplicationUnsetCommand returns a new instance of an `argocd app unset` command
func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
appOpts := cmdutil.AppOptions{}
@@ -723,9 +708,7 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
command.Flags().BoolVar(&opts.nameSuffix, "namesuffix", false, "Kustomize namesuffix")
command.Flags().BoolVar(&opts.namePrefix, "nameprefix", false, "Kustomize nameprefix")
command.Flags().BoolVar(&opts.kustomizeVersion, "kustomize-version", false, "Kustomize version")
command.Flags().BoolVar(&opts.kustomizeNamespace, "kustomize-namespace", false, "Kustomize namespace")
command.Flags().StringArrayVar(&opts.kustomizeImages, "kustomize-image", []string{}, "Kustomize images name (e.g. --kustomize-image node --kustomize-image mysql)")
command.Flags().StringArrayVar(&opts.kustomizeReplicas, "kustomize-replica", []string{}, "Kustomize replicas name (e.g. --kustomize-replica my-deployment --kustomize-replica my-statefulset)")
command.Flags().StringArrayVar(&opts.pluginEnvs, "plugin-env", []string{}, "Unset plugin env variables (e.g --plugin-env name)")
command.Flags().BoolVar(&opts.passCredentials, "pass-credentials", false, "Unset passCredentials")
return command
@@ -733,7 +716,7 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
func unset(source *argoappv1.ApplicationSource, opts unsetOpts) (updated bool, nothingToUnset bool) {
if source.Kustomize != nil {
if opts.KustomizeIsZero() {
if !opts.namePrefix && !opts.nameSuffix && !opts.kustomizeVersion && len(opts.kustomizeImages) == 0 {
return false, true
}
@@ -752,11 +735,6 @@ func unset(source *argoappv1.ApplicationSource, opts unsetOpts) (updated bool, n
source.Kustomize.Version = ""
}
if opts.kustomizeNamespace && source.Kustomize.Namespace != "" {
updated = true
source.Kustomize.Namespace = ""
}
for _, kustomizeImage := range opts.kustomizeImages {
for i, item := range source.Kustomize.Images {
if argoappv1.KustomizeImage(kustomizeImage).Match(item) {
@@ -770,17 +748,6 @@ func unset(source *argoappv1.ApplicationSource, opts unsetOpts) (updated bool, n
}
}
}
for _, kustomizeReplica := range opts.kustomizeReplicas {
kustomizeReplicas := source.Kustomize.Replicas
for i, item := range kustomizeReplicas {
if kustomizeReplica == item.Name {
source.Kustomize.Replicas = append(kustomizeReplicas[0:i], kustomizeReplicas[i+1:]...)
updated = true
break
}
}
}
}
if source.Helm != nil {
if len(opts.parameters) == 0 && len(opts.valuesFiles) == 0 && !opts.valuesLiteral && !opts.ignoreMissingValueFiles && !opts.passCredentials {
@@ -1025,7 +992,7 @@ func findandPrintDiff(ctx context.Context, app *argoappv1.Application, resources
items := make([]objKeyLiveTarget, 0)
if diffOptions.local != "" {
localObjs := groupObjsByKey(getLocalObjects(ctx, app, diffOptions.local, diffOptions.localRepoRoot, argoSettings.AppLabelKey, diffOptions.cluster.Info.ServerVersion, diffOptions.cluster.Info.APIVersions, argoSettings.KustomizeOptions, argoSettings.ConfigManagementPlugins, argoSettings.TrackingMethod), liveObjs, app.Spec.Destination.Namespace)
items = groupObjsForDiff(resources, localObjs, items, argoSettings, app.InstanceName(argoSettings.ControllerNamespace), app.Spec.Destination.Namespace)
items = groupObjsForDiff(resources, localObjs, items, argoSettings, app.InstanceName(argoSettings.ControllerNamespace))
} else if diffOptions.revision != "" {
var unstructureds []*unstructured.Unstructured
for _, mfst := range diffOptions.res.Manifests {
@@ -1034,7 +1001,7 @@ func findandPrintDiff(ctx context.Context, app *argoappv1.Application, resources
unstructureds = append(unstructureds, obj)
}
groupedObjs := groupObjsByKey(unstructureds, liveObjs, app.Spec.Destination.Namespace)
items = groupObjsForDiff(resources, groupedObjs, items, argoSettings, app.InstanceName(argoSettings.ControllerNamespace), app.Spec.Destination.Namespace)
items = groupObjsForDiff(resources, groupedObjs, items, argoSettings, app.Name)
} else if diffOptions.serversideRes != nil {
var unstructureds []*unstructured.Unstructured
for _, mfst := range diffOptions.serversideRes.Manifests {
@@ -1043,7 +1010,7 @@ func findandPrintDiff(ctx context.Context, app *argoappv1.Application, resources
unstructureds = append(unstructureds, obj)
}
groupedObjs := groupObjsByKey(unstructureds, liveObjs, app.Spec.Destination.Namespace)
items = groupObjsForDiff(resources, groupedObjs, items, argoSettings, app.InstanceName(argoSettings.ControllerNamespace), app.Spec.Destination.Namespace)
items = groupObjsForDiff(resources, groupedObjs, items, argoSettings, app.Name)
} else {
for i := range resources.Items {
res := resources.Items[i]
@@ -1103,7 +1070,7 @@ func findandPrintDiff(ctx context.Context, app *argoappv1.Application, resources
return foundDiffs
}
func groupObjsForDiff(resources *application.ManagedResourcesResponse, objs map[kube.ResourceKey]*unstructured.Unstructured, items []objKeyLiveTarget, argoSettings *settings.Settings, appName, namespace string) []objKeyLiveTarget {
func groupObjsForDiff(resources *application.ManagedResourcesResponse, objs map[kube.ResourceKey]*unstructured.Unstructured, items []objKeyLiveTarget, argoSettings *settings.Settings, appName string) []objKeyLiveTarget {
resourceTracking := argo.NewResourceTracking()
for _, res := range resources.Items {
var live = &unstructured.Unstructured{}
@@ -1118,7 +1085,7 @@ func groupObjsForDiff(resources *application.ManagedResourcesResponse, objs map[
}
if local, ok := objs[key]; ok || live != nil {
if local != nil && !kube.IsCRD(local) {
err = resourceTracking.SetAppInstance(local, argoSettings.AppLabelKey, appName, namespace, argoappv1.TrackingMethod(argoSettings.GetTrackingMethod()))
err = resourceTracking.SetAppInstance(local, argoSettings.AppLabelKey, appName, "", argoappv1.TrackingMethod(argoSettings.GetTrackingMethod()))
errors.CheckError(err)
}
@@ -1378,7 +1345,6 @@ const (
resourceFieldCount = 3
resourceFieldNamespaceDelimiter = "/"
resourceFieldNameWithNamespaceCount = 2
resourceExcludeIndicator = "!"
)
// resource is GROUP:KIND:NAMESPACE/NAME or GROUP:KIND:NAME
@@ -1403,12 +1369,6 @@ func parseSelectedResources(resources []string) ([]*argoappv1.SyncOperationResou
}
for _, resource := range resources {
isExcluded := false
// check if the resource flag starts with a '!'
if strings.HasPrefix(resource, resourceExcludeIndicator) {
resource = strings.TrimPrefix(resource, resourceExcludeIndicator)
isExcluded = true
}
fields := strings.Split(resource, resourceFieldDelimiter)
if len(fields) != resourceFieldCount {
return nil, fmt.Errorf("Resource should have GROUP%sKIND%sNAME, but instead got: %s", resourceFieldDelimiter, resourceFieldDelimiter, resource)
@@ -1422,7 +1382,6 @@ func parseSelectedResources(resources []string) ([]*argoappv1.SyncOperationResou
Kind: fields[1],
Name: name,
Namespace: namespace,
Exclude: isExcluded,
})
}
return selectedResources, nil
@@ -1457,16 +1416,6 @@ func NewApplicationWaitCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
# Wait for multiple apps
argocd app wait my-app other-app
# Wait for apps by resource
# Resource should be formatted as GROUP:KIND:NAME. If no GROUP is specified then :KIND:NAME.
argocd app wait my-app --resource :Service:my-service
argocd app wait my-app --resource argoproj.io:Rollout:my-rollout
argocd app wait my-app --resource '!apps:Deployment:my-service'
argocd app wait my-app --resource apps:Deployment:my-service --resource :Service:my-service
argocd app wait my-app --resource '!*:Service:*'
# Specify namespace if the application has resources with the same name in different namespaces
argocd app wait my-app --resource argoproj.io:Rollout:my-namespace/my-rollout
# Wait for apps by label, in this example we waiting for apps that are children of another app (aka app-of-apps)
argocd app wait -l app.kubernetes.io/instance=my-app
argocd app wait -l app.kubernetes.io/instance!=my-app
@@ -1495,7 +1444,7 @@ func NewApplicationWaitCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
}
}
for _, appName := range appNames {
_, _, err := waitOnApplicationStatus(ctx, acdClient, appName, timeout, watch, selectedResources)
_, err := waitOnApplicationStatus(ctx, acdClient, appName, timeout, watch, selectedResources)
errors.CheckError(err)
}
},
@@ -1505,7 +1454,7 @@ func NewApplicationWaitCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
command.Flags().BoolVar(&watch.suspended, "suspended", false, "Wait for suspended")
command.Flags().BoolVar(&watch.degraded, "degraded", false, "Wait for degraded")
command.Flags().StringVarP(&selector, "selector", "l", "", "Wait for apps by label. Supports '=', '==', '!=', in, notin, exists & not exists. Matching apps must satisfy all of the specified label constraints.")
command.Flags().StringArrayVar(&resources, "resource", []string{}, fmt.Sprintf("Sync only specific resources as GROUP%[1]sKIND%[1]sNAME or %[2]sGROUP%[1]sKIND%[1]sNAME. Fields may be blank and '*' can be used. This option may be specified repeatedly", resourceFieldDelimiter, resourceExcludeIndicator))
command.Flags().StringArrayVar(&resources, "resource", []string{}, fmt.Sprintf("Sync only specific resources as GROUP%sKIND%sNAME. Fields may be blank. This option may be specified repeatedly", resourceFieldDelimiter, resourceFieldDelimiter))
command.Flags().BoolVar(&watch.operation, "operation", false, "Wait for pending operations")
command.Flags().UintVar(&timeout, "timeout", defaultCheckTimeoutSeconds, "Time out after this many seconds")
return command
@@ -1565,9 +1514,6 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
# Resource should be formatted as GROUP:KIND:NAME. If no GROUP is specified then :KIND:NAME
argocd app sync my-app --resource :Service:my-service
argocd app sync my-app --resource argoproj.io:Rollout:my-rollout
argocd app sync my-app --resource '!apps:Deployment:my-service'
argocd app sync my-app --resource apps:Deployment:my-service --resource :Service:my-service
argocd app sync my-app --resource '!*:Service:*'
# Specify namespace if the application has resources with the same name in different namespaces
argocd app sync my-app --resource argoproj.io:Rollout:my-namespace/my-rollout`,
Run: func(c *cobra.Command, args []string) {
@@ -1659,23 +1605,8 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
errors.CheckError(err)
if app.Spec.HasMultipleSources() {
if revision != "" {
log.Fatal("argocd cli does not work on multi-source app with --revision flag")
return
}
if local != "" {
log.Fatal("argocd cli does not work on multi-source app with --local flag")
return
}
}
// filters out only those resources that needs to be synced
filteredResources := filterAppResources(app, selectedResources)
// if resources are provided and no app resources match, then return error
if len(resources) > 0 && len(filteredResources) == 0 {
log.Fatalf("No matching app resources found for resource filter: %v", strings.Join(resources, ", "))
log.Fatal("argocd cli does not work on multi-source app")
return
}
if local != "" {
@@ -1724,7 +1655,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
AppNamespace: &appNs,
DryRun: &dryRun,
Revision: &revision,
Resources: filteredResources,
Resources: selectedResources,
Prune: &prune,
Manifests: localObjsStrings,
Infos: getInfos(infos),
@@ -1779,15 +1710,15 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
errors.CheckError(err)
if !async {
app, opState, err := waitOnApplicationStatus(ctx, acdClient, appQualifiedName, timeout, watchOpts{operation: true}, selectedResources)
app, err := waitOnApplicationStatus(ctx, acdClient, appQualifiedName, timeout, watchOpts{operation: true}, selectedResources)
errors.CheckError(err)
if !dryRun {
if !opState.Phase.Successful() {
log.Fatalf("Operation has completed with phase: %s", opState.Phase)
if !app.Status.OperationState.Phase.Successful() {
log.Fatalf("Operation has completed with phase: %s", app.Status.OperationState.Phase)
} else if len(selectedResources) == 0 && app.Status.Sync.Status != argoappv1.SyncStatusCodeSynced {
// Only get resources to be pruned if sync was application-wide and final status is not synced
pruningRequired := opState.SyncResult.Resources.PruningRequired()
pruningRequired := app.Status.OperationState.SyncResult.Resources.PruningRequired()
if pruningRequired > 0 {
log.Fatalf("%d resources require pruning", pruningRequired)
}
@@ -1800,7 +1731,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
command.Flags().BoolVar(&dryRun, "dry-run", false, "Preview apply without affecting cluster")
command.Flags().BoolVar(&prune, "prune", false, "Allow deleting unexpected resources")
command.Flags().StringVar(&revision, "revision", "", "Sync to a specific revision. Preserves parameter overrides")
command.Flags().StringArrayVar(&resources, "resource", []string{}, fmt.Sprintf("Sync only specific resources as GROUP%[1]sKIND%[1]sNAME or %[2]sGROUP%[1]sKIND%[1]sNAME. Fields may be blank and '*' can be used. This option may be specified repeatedly", resourceFieldDelimiter, resourceExcludeIndicator))
command.Flags().StringArrayVar(&resources, "resource", []string{}, fmt.Sprintf("Sync only specific resources as GROUP%sKIND%sNAME. Fields may be blank. This option may be specified repeatedly", resourceFieldDelimiter, resourceFieldDelimiter))
command.Flags().StringVarP(&selector, "selector", "l", "", "Sync apps that match this label. Supports '=', '==', '!=', in, notin, exists & not exists. Matching apps must satisfy all of the specified label constraints.")
command.Flags().StringArrayVar(&labels, "label", []string{}, "Sync only specific resources with a label. This option may be specified repeatedly.")
command.Flags().UintVar(&timeout, "timeout", defaultCheckTimeoutSeconds, "Time out after this many seconds")
@@ -1925,9 +1856,15 @@ func getResourceStates(app *argoappv1.Application, selectedResources []*argoappv
}
// filter out not selected resources
if len(selectedResources) > 0 {
r := []argoappv1.SyncOperationResource{}
for _, res := range selectedResources {
if res != nil {
r = append(r, *res)
}
}
for i := len(states) - 1; i >= 0; i-- {
res := states[i]
if !argo.IncludeResource(res.Name, res.Namespace, schema.GroupVersionKind{Group: res.Group, Kind: res.Kind}, selectedResources) {
if !argo.ContainsSyncResource(res.Name, res.Namespace, schema.GroupVersionKind{Group: res.Group, Kind: res.Kind}, r) {
states = append(states[:i], states[i+1:]...)
}
}
@@ -1935,26 +1872,6 @@ func getResourceStates(app *argoappv1.Application, selectedResources []*argoappv
return states
}
// filterAppResources selects the app resources that match atleast one of the resource filters.
func filterAppResources(app *argoappv1.Application, selectedResources []*argoappv1.SyncOperationResource) []*argoappv1.SyncOperationResource {
var filteredResources []*argoappv1.SyncOperationResource
if app != nil && len(selectedResources) > 0 {
for i := range app.Status.Resources {
appResource := app.Status.Resources[i]
if (argo.IncludeResource(appResource.Name, appResource.Namespace,
schema.GroupVersionKind{Group: appResource.Group, Kind: appResource.Kind}, selectedResources)) {
filteredResources = append(filteredResources, &argoappv1.SyncOperationResource{
Group: appResource.Group,
Kind: appResource.Kind,
Name: appResource.Name,
Namespace: appResource.Namespace,
})
}
}
}
return filteredResources
}
func groupResourceStates(app *argoappv1.Application, selectedResources []*argoappv1.SyncOperationResource) map[string]*resourceState {
resStates := make(map[string]*resourceState)
for _, result := range getResourceStates(app, selectedResources) {
@@ -2001,10 +1918,7 @@ func checkResourceStatus(watch watchOpts, healthStatus string, syncStatus string
const waitFormatString = "%s\t%5s\t%10s\t%10s\t%20s\t%8s\t%7s\t%10s\t%s\n"
// waitOnApplicationStatus watches an application and blocks until either the desired watch conditions
// are fulfiled or we reach the timeout. Returns the app once desired conditions have been filled.
// Additionally return the operationState at time of fulfilment (which may be different than returned app).
func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, appName string, timeout uint, watch watchOpts, selectedResources []*argoappv1.SyncOperationResource) (*argoappv1.Application, *argoappv1.OperationState, error) {
func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, appName string, timeout uint, watch watchOpts, selectedResources []*argoappv1.SyncOperationResource) (*argoappv1.Application, error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
@@ -2062,20 +1976,10 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client,
AppNamespace: &appNs,
})
errors.CheckError(err)
// printFinalStatus() will refresh and update the app object, potentially causing the app's
// status.operationState to be different than the version when we break out of the event loop.
// This means the app.status is unreliable for determining the final state of the operation.
// finalOperationState captures the operationState as it was seen when we met the conditions of
// the wait, so the caller can rely on it to determine the outcome of the operation.
// See: https://github.com/argoproj/argo-cd/issues/5592
finalOperationState := app.Status.OperationState
appEventCh := acdClient.WatchApplicationWithRetry(ctx, appName, app.ResourceVersion)
for appEvent := range appEventCh {
app = &appEvent.Application
finalOperationState = app.Status.OperationState
operationInProgress := false
// consider the operation is in progress
if app.Operation != nil {
@@ -2113,7 +2017,7 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client,
if selectedResourcesAreReady && (!operationInProgress || !watch.operation) {
app = printFinalStatus(app)
return app, finalOperationState, nil
return app, nil
}
newStates := groupResourceStates(app, selectedResources)
@@ -2123,7 +2027,7 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client,
if prevState, found := prevStates[stateKey]; found {
if watch.health && prevState.Health != string(health.HealthStatusUnknown) && prevState.Health != string(health.HealthStatusDegraded) && newState.Health == string(health.HealthStatusDegraded) {
_ = printFinalStatus(app)
return nil, finalOperationState, fmt.Errorf("application '%s' health state has transitioned from %s to %s", appName, prevState.Health, newState.Health)
return nil, fmt.Errorf("application '%s' health state has transitioned from %s to %s", appName, prevState.Health, newState.Health)
}
doPrint = prevState.Merge(newState)
} else {
@@ -2137,7 +2041,7 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client,
_ = w.Flush()
}
_ = printFinalStatus(app)
return nil, finalOperationState, fmt.Errorf("timed out (%ds) waiting for app %q match desired state", timeout, appName)
return nil, fmt.Errorf("timed out (%ds) waiting for app %q match desired state", timeout, appName)
}
// setParameterOverrides updates an existing or appends a new parameter override in the application
@@ -2293,7 +2197,7 @@ func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobr
})
errors.CheckError(err)
_, _, err = waitOnApplicationStatus(ctx, acdClient, app.QualifiedName(), timeout, watchOpts{
_, err = waitOnApplicationStatus(ctx, acdClient, app.QualifiedName(), timeout, watchOpts{
operation: true,
}, nil)
errors.CheckError(err)
@@ -2505,11 +2409,12 @@ func NewApplicationPatchCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
command := cobra.Command{
Use: "patch APPNAME",
Short: "Patch application",
Example: ` # Update an application's source path using json patch
argocd app patch myapplication --patch='[{"op": "replace", "path": "/spec/source/path", "value": "newPath"}]' --type json
Long: `Examples:
# Update an application's source path using json patch
argocd app patch myapplication --patch='[{"op": "replace", "path": "/spec/source/path", "value": "newPath"}]' --type json
# Update an application's repository target revision using merge patch
argocd app patch myapplication --patch '{"spec": { "source": { "targetRevision": "master" } }}' --type merge`,
# Update an application's repository target revision using merge patch
argocd app patch myapplication --patch '{"spec": { "source": { "targetRevision": "master" } }}' --type merge`,
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()

View File

@@ -18,7 +18,6 @@ import (
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
applicationpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/application"
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
v1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/argo"
"github.com/argoproj/argo-cd/v2/util/errors"
@@ -203,7 +202,7 @@ func getActionableResourcesForApplication(appIf applicationpkg.ApplicationServic
if err != nil {
return nil, err
}
app.Kind = application.ApplicationKind
app.Kind = "Application"
app.APIVersion = "argoproj.io/v1alpha1"
appManifest, err := json.Marshal(app)
if err != nil {

View File

@@ -7,7 +7,6 @@ import (
"time"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/gitops-engine/pkg/health"
@@ -17,7 +16,6 @@ import (
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/util/intstr"
)
func Test_getInfos(t *testing.T) {
@@ -752,16 +750,6 @@ func Test_unset(t *testing.T) {
"old1=new:tag",
"old2=new:tag",
},
Replicas: []v1alpha1.KustomizeReplica{
{
Name: "my-deployment",
Count: intstr.FromInt(2),
},
{
Name: "my-statefulset",
Count: intstr.FromInt(4),
},
},
},
}
@@ -838,15 +826,6 @@ func Test_unset(t *testing.T) {
assert.False(t, updated)
assert.False(t, nothingToUnset)
assert.Equal(t, 2, len(kustomizeSource.Kustomize.Replicas))
updated, nothingToUnset = unset(kustomizeSource, unsetOpts{kustomizeReplicas: []string{"my-deployment"}})
assert.Equal(t, 1, len(kustomizeSource.Kustomize.Replicas))
assert.True(t, updated)
assert.False(t, nothingToUnset)
updated, nothingToUnset = unset(kustomizeSource, unsetOpts{kustomizeReplicas: []string{"my-deployment"}})
assert.False(t, updated)
assert.False(t, nothingToUnset)
assert.Equal(t, 2, len(helmSource.Helm.Parameters))
updated, nothingToUnset = unset(helmSource, unsetOpts{parameters: []string{"name-1"}})
assert.Equal(t, 1, len(helmSource.Helm.Parameters))
@@ -925,252 +904,22 @@ func Test_unset_nothingToUnset(t *testing.T) {
}
}
func TestFilterAppResources(t *testing.T) {
// App resources
var (
appReplicaSet1 = v1alpha1.ResourceStatus{
Group: "apps",
Kind: "ReplicaSet",
Namespace: "default",
Name: "replicaSet-name1",
}
appReplicaSet2 = v1alpha1.ResourceStatus{
Group: "apps",
Kind: "ReplicaSet",
Namespace: "default",
Name: "replicaSet-name2",
}
appJob = v1alpha1.ResourceStatus{
Group: "batch",
Kind: "Job",
Namespace: "default",
Name: "job-name",
}
appService1 = v1alpha1.ResourceStatus{
Group: "",
Kind: "Service",
Namespace: "default",
Name: "service-name1",
}
appService2 = v1alpha1.ResourceStatus{
Group: "",
Kind: "Service",
Namespace: "default",
Name: "service-name2",
}
appDeployment = v1alpha1.ResourceStatus{
Group: "apps",
Kind: "Deployment",
Namespace: "default",
Name: "deployment-name",
}
)
app := v1alpha1.Application{
Status: v1alpha1.ApplicationStatus{
Resources: []v1alpha1.ResourceStatus{
appReplicaSet1, appReplicaSet2, appJob, appService1, appService2, appDeployment},
},
}
// Resource filters
var (
blankValues = argoappv1.SyncOperationResource{
Group: "",
Kind: "",
Name: "",
Namespace: "",
Exclude: false}
// *:*:*
includeAllResources = argoappv1.SyncOperationResource{
Group: "*",
Kind: "*",
Name: "*",
Namespace: "",
Exclude: false}
// !*:*:*
excludeAllResources = argoappv1.SyncOperationResource{
Group: "*",
Kind: "*",
Name: "*",
Namespace: "",
Exclude: true}
// *:Service:*
includeAllServiceResources = argoappv1.SyncOperationResource{
Group: "*",
Kind: "Service",
Name: "*",
Namespace: "",
Exclude: false}
// !*:Service:*
excludeAllServiceResources = argoappv1.SyncOperationResource{
Group: "*",
Kind: "Service",
Name: "*",
Namespace: "",
Exclude: true}
// apps:ReplicaSet:replicaSet-name1
includeReplicaSet1Resource = argoappv1.SyncOperationResource{
Group: "apps",
Kind: "ReplicaSet",
Name: "replicaSet-name1",
Namespace: "",
Exclude: false}
// !apps:ReplicaSet:replicaSet-name2
excludeReplicaSet2Resource = argoappv1.SyncOperationResource{
Group: "apps",
Kind: "ReplicaSet",
Name: "replicaSet-name2",
Namespace: "",
Exclude: true}
)
// Filtered resources
var (
replicaSet1 = v1alpha1.SyncOperationResource{
Group: "apps",
Kind: "ReplicaSet",
Namespace: "default",
Name: "replicaSet-name1",
}
replicaSet2 = v1alpha1.SyncOperationResource{
Group: "apps",
Kind: "ReplicaSet",
Namespace: "default",
Name: "replicaSet-name2",
}
job = v1alpha1.SyncOperationResource{
Group: "batch",
Kind: "Job",
Namespace: "default",
Name: "job-name",
}
service1 = v1alpha1.SyncOperationResource{
Group: "",
Kind: "Service",
Namespace: "default",
Name: "service-name1",
}
service2 = v1alpha1.SyncOperationResource{
Group: "",
Kind: "Service",
Namespace: "default",
Name: "service-name2",
}
deployment = v1alpha1.SyncOperationResource{
Group: "apps",
Kind: "Deployment",
Namespace: "default",
Name: "deployment-name",
}
)
tests := []struct {
testName string
selectedResources []*argoappv1.SyncOperationResource
expectedResult []*argoappv1.SyncOperationResource
}{
//--resource apps:ReplicaSet:replicaSet-name1 --resource *:Service:*
{testName: "Include ReplicaSet replicaSet-name1 resouce and all service resources",
selectedResources: []*argoappv1.SyncOperationResource{&includeAllServiceResources, &includeReplicaSet1Resource},
expectedResult: []*argoappv1.SyncOperationResource{&replicaSet1, &service1, &service2},
},
//--resource apps:ReplicaSet:replicaSet-name1 --resource !*:Service:*
{testName: "Include ReplicaSet replicaSet-name1 resouce and exclude all service resources",
selectedResources: []*argoappv1.SyncOperationResource{&excludeAllServiceResources, &includeReplicaSet1Resource},
expectedResult: []*argoappv1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &deployment},
},
// --resource !apps:ReplicaSet:replicaSet-name2 --resource !*:Service:*
{testName: "Exclude ReplicaSet replicaSet-name2 resouce and all service resources",
selectedResources: []*argoappv1.SyncOperationResource{&excludeReplicaSet2Resource, &excludeAllServiceResources},
expectedResult: []*argoappv1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &service1, &service2, &deployment},
},
// --resource !apps:ReplicaSet:replicaSet-name2
{testName: "Exclude ReplicaSet replicaSet-name2 resouce",
selectedResources: []*argoappv1.SyncOperationResource{&excludeReplicaSet2Resource},
expectedResult: []*argoappv1.SyncOperationResource{&replicaSet1, &job, &service1, &service2, &deployment},
},
// --resource apps:ReplicaSet:replicaSet-name1
{testName: "Include ReplicaSet replicaSet-name1 resouce",
selectedResources: []*argoappv1.SyncOperationResource{&includeReplicaSet1Resource},
expectedResult: []*argoappv1.SyncOperationResource{&replicaSet1},
},
// --resource !*:Service:*
{testName: "Exclude Service resouces",
selectedResources: []*argoappv1.SyncOperationResource{&excludeAllServiceResources},
expectedResult: []*argoappv1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &deployment},
},
// --resource *:Service:*
{testName: "Include Service resouces",
selectedResources: []*argoappv1.SyncOperationResource{&includeAllServiceResources},
expectedResult: []*argoappv1.SyncOperationResource{&service1, &service2},
},
// --resource !*:*:*
{testName: "Exclude all resouces",
selectedResources: []*argoappv1.SyncOperationResource{&excludeAllResources},
expectedResult: nil,
},
// --resource *:*:*
{testName: "Include all resouces",
selectedResources: []*argoappv1.SyncOperationResource{&includeAllResources},
expectedResult: []*argoappv1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &service1, &service2, &deployment},
},
{testName: "No Filters",
selectedResources: []*argoappv1.SyncOperationResource{&blankValues},
expectedResult: nil,
},
{testName: "Empty Filter",
selectedResources: []*argoappv1.SyncOperationResource{},
expectedResult: nil,
},
}
for _, test := range tests {
t.Run(test.testName, func(t *testing.T) {
filteredResources := filterAppResources(&app, test.selectedResources)
assert.Equal(t, test.expectedResult, filteredResources)
})
}
}
func TestParseSelectedResources(t *testing.T) {
resources := []string{"v1alpha:Application:test",
"v1alpha:Application:namespace/test",
"!v1alpha:Application:test",
"apps:Deployment:default/test",
"!*:*:*"}
resources := []string{"v1alpha:Application:test", "v1alpha:Application:namespace/test"}
operationResources, err := parseSelectedResources(resources)
assert.NoError(t, err)
assert.Len(t, operationResources, 5)
assert.Len(t, operationResources, 2)
assert.Equal(t, *operationResources[0], v1alpha1.SyncOperationResource{
Namespace: "",
Name: "test",
Kind: application.ApplicationKind,
Kind: "Application",
Group: "v1alpha",
})
assert.Equal(t, *operationResources[1], v1alpha1.SyncOperationResource{
Namespace: "namespace",
Name: "test",
Kind: application.ApplicationKind,
Group: "v1alpha",
})
assert.Equal(t, *operationResources[2], v1alpha1.SyncOperationResource{
Namespace: "",
Name: "test",
Kind: "Application",
Group: "v1alpha",
Exclude: true,
})
assert.Equal(t, *operationResources[3], v1alpha1.SyncOperationResource{
Namespace: "default",
Name: "test",
Kind: "Deployment",
Group: "apps",
Exclude: false,
})
assert.Equal(t, *operationResources[4], v1alpha1.SyncOperationResource{
Namespace: "",
Name: "*",
Kind: "*",
Group: "*",
Exclude: true,
})
}

View File

@@ -345,7 +345,7 @@ func printAppSetSummaryTable(appSet *arogappsetv1.ApplicationSet) {
var (
syncPolicyStr string
syncPolicy = appSet.Spec.Template.Spec.SyncPolicy
syncPolicy = appSet.Spec.Template.Spec.SyncPolicy
)
if syncPolicy != nil && syncPolicy.Automated != nil {
syncPolicyStr = "Automated"

View File

@@ -1,7 +1,7 @@
package commands
import (
"io"
"io/ioutil"
"os"
"testing"
@@ -185,7 +185,7 @@ SyncPolicy: Automated
printAppSetSummaryTable(tt.appSet)
w.Close()
out, err := io.ReadAll(r)
out, err := ioutil.ReadAll(r)
assert.NoError(t, err)
assert.Equal(t, tt.expectedOutput, string(out))
})

View File

@@ -27,17 +27,6 @@ import (
"github.com/argoproj/argo-cd/v2/util/text/label"
)
const (
// type of the cluster ID is 'name'
clusterIdTypeName = "name"
// cluster field is 'name'
clusterFieldName = "name"
// cluster field is 'namespaces'
clusterFieldNamespaces = "namespaces"
// indicates managing all namespaces
allNamespaces = "*"
)
// NewClusterCommand returns a new instance of an `argocd cluster` command
func NewClusterCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clientcmd.PathOptions) *cobra.Command {
var command = &cobra.Command{
@@ -58,10 +47,7 @@ func NewClusterCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clientc
# Remove a target cluster context from ArgoCD
argocd cluster rm example-cluster
# Set a target cluster context from ArgoCD
argocd cluster set CLUSTER_NAME --name new-cluster-name --namespace '*'
argocd cluster set CLUSTER_NAME --name new-cluster-name --namespace namespace-one --namespace namespace-two`,
`,
}
command.AddCommand(NewClusterAddCommand(clientOpts, pathOpts))
@@ -69,7 +55,6 @@ func NewClusterCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clientc
command.AddCommand(NewClusterListCommand(clientOpts))
command.AddCommand(NewClusterRemoveCommand(clientOpts, pathOpts))
command.AddCommand(NewClusterRotateAuthCommand(clientOpts))
command.AddCommand(NewClusterSetCommand(clientOpts))
return command
}
@@ -93,17 +78,9 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
cmdutil.PrintKubeContexts(configAccess)
os.Exit(1)
}
if clusterOpts.InCluster && clusterOpts.ClusterEndpoint != "" {
log.Fatal("Can only use one of --in-cluster or --cluster-endpoint")
return
}
contextName := args[0]
conf, err := getRestConfig(pathOpts, contextName)
errors.CheckError(err)
clientset, err := kubernetes.NewForConfig(conf)
errors.CheckError(err)
managerBearerToken := ""
var awsAuthConf *argoappv1.AWSAuthConfig
var execProviderConf *argoappv1.ExecProviderConfig
@@ -122,10 +99,13 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
}
} else {
// Install RBAC resources for managing the cluster
clientset, err := kubernetes.NewForConfig(conf)
errors.CheckError(err)
if clusterOpts.ServiceAccount != "" {
managerBearerToken, err = clusterauth.GetServiceAccountBearerToken(clientset, clusterOpts.SystemNamespace, clusterOpts.ServiceAccount, common.BearerTokenTimeout)
} else {
isTerminal := isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())
if isTerminal && !skipConfirmation {
accessLevel := "cluster"
if len(clusterOpts.Namespaces) > 0 {
@@ -152,18 +132,9 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
contextName = clusterOpts.Name
}
clst := cmdutil.NewCluster(contextName, clusterOpts.Namespaces, clusterOpts.ClusterResources, conf, managerBearerToken, awsAuthConf, execProviderConf, labelsMap, annotationsMap)
if clusterOpts.InClusterEndpoint() {
if clusterOpts.InCluster {
clst.Server = argoappv1.KubernetesInternalAPIServerAddr
} else if clusterOpts.ClusterEndpoint == string(cmdutil.KubePublicEndpoint) {
endpoint, err := cmdutil.GetKubePublicEndpoint(clientset)
if err != nil || len(endpoint) == 0 {
log.Warnf("Failed to find the cluster endpoint from kube-public data: %v", err)
log.Infof("Falling back to the endpoint '%s' as listed in the kubeconfig context", clst.Server)
endpoint = clst.Server
}
clst.Server = endpoint
}
if clusterOpts.Shard >= 0 {
clst.Shard = &clusterOpts.Shard
}
@@ -214,72 +185,6 @@ func getRestConfig(pathOpts *clientcmd.PathOptions, ctxName string) (*rest.Confi
return conf, nil
}
// NewClusterSetCommand returns a new instance of an `argocd cluster set` command
func NewClusterSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
clusterOptions cmdutil.ClusterOptions
clusterName string
)
var command = &cobra.Command{
Use: "set NAME",
Short: "Set cluster information",
Example: ` # Set cluster information
argocd cluster set CLUSTER_NAME --name new-cluster-name --namespace '*'
argocd cluster set CLUSTER_NAME --name new-cluster-name --namespace namespace-one --namespace namespace-two`,
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
if len(args) != 1 {
c.HelpFunc()(c, args)
os.Exit(1)
}
// name of the cluster whose fields have to be updated.
clusterName = args[0]
conn, clusterIf := headless.NewClientOrDie(clientOpts, c).NewClusterClientOrDie()
defer io.Close(conn)
// checks the fields that needs to be updated
updatedFields := checkFieldsToUpdate(clusterOptions)
namespaces := clusterOptions.Namespaces
// check if all namespaces have to be considered
if len(namespaces) == 1 && strings.EqualFold(namespaces[0], allNamespaces) {
namespaces[0] = ""
}
if updatedFields != nil {
clusterUpdateRequest := clusterpkg.ClusterUpdateRequest{
Cluster: &argoappv1.Cluster{
Name: clusterOptions.Name,
Namespaces: namespaces,
},
UpdatedFields: updatedFields,
Id: &clusterpkg.ClusterID{
Type: clusterIdTypeName,
Value: clusterName,
},
}
_, err := clusterIf.Update(ctx, &clusterUpdateRequest)
errors.CheckError(err)
fmt.Printf("Cluster '%s' updated.\n", clusterName)
} else {
fmt.Print("Specify the cluster field to be updated.\n")
}
},
}
command.Flags().StringVar(&clusterOptions.Name, "name", "", "Overwrite the cluster name")
command.Flags().StringArrayVar(&clusterOptions.Namespaces, "namespace", nil, "List of namespaces which are allowed to manage. Specify '*' to manage all namespaces")
return command
}
// checkFieldsToUpdate returns the fields that needs to be updated
func checkFieldsToUpdate(clusterOptions cmdutil.ClusterOptions) []string {
var updatedFields []string
if clusterOptions.Name != "" {
updatedFields = append(updatedFields, clusterFieldName)
}
if clusterOptions.Namespaces != nil {
updatedFields = append(updatedFields, clusterFieldNamespaces)
}
return updatedFields
}
// NewClusterGetCommand returns a new instance of an `argocd cluster get` command
func NewClusterGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (

View File

@@ -146,7 +146,6 @@ __argocd_custom_func() {
;;
argocd_cluster_get | \
argocd_cluster_rm | \
argocd_cluster_set | \
argocd_login | \
argocd_cluster_add)
__argocd_list_servers
@@ -204,13 +203,8 @@ To access completions in your current shell, run
$ source <(argocd completion bash)
Alternatively, write it to a file and source in .bash_profile
For zsh, add the following to your ~/.zshrc file:
source <(argocd completion zsh)
compdef _argocd argocd
Optionally, also add the following, in case you are getting errors involving compdef & compinit such as command not found: compdef:
autoload -Uz compinit
compinit
For zsh, output to a file in a directory referenced by the $fpath shell
variable.
`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {

View File

@@ -13,8 +13,8 @@ import (
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/initialize"
"github.com/alicebob/miniredis/v2"
"github.com/go-redis/redis/v8"
"github.com/golang/protobuf/ptypes/empty"
"github.com/redis/go-redis/v9"
log "github.com/sirupsen/logrus"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/util/runtime"
@@ -38,12 +38,11 @@ import (
)
type forwardCacheClient struct {
namespace string
context string
init sync.Once
client cache.CacheClient
compression cache.RedisCompressionType
err error
namespace string
context string
init sync.Once
client cache.CacheClient
err error
}
func (c *forwardCacheClient) doLazy(action func(client cache.CacheClient) error) error {
@@ -59,7 +58,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, c.compression)
c.client = cache.NewRedisCache(redisClient, time.Hour, cache.RedisCompressionNone)
})
if c.err != nil {
return c.err
@@ -140,7 +139,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, compression cache.RedisCompressionType) error {
func StartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions, ctxStr string, port *int, address *string) error {
flags := pflag.NewFlagSet("tmp", pflag.ContinueOnError)
clientConfig := cli.AddKubectlFlagsToSet(flags)
startInProcessAPI := clientOpts.Core
@@ -201,7 +200,7 @@ func StartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions,
if err != nil {
return err
}
appstateCache := appstatecache.NewCache(cache.NewCache(&forwardCacheClient{namespace: namespace, context: ctxStr, compression: compression}), time.Hour)
appstateCache := appstatecache.NewCache(cache.NewCache(&forwardCacheClient{namespace: namespace, context: ctxStr}), time.Hour)
srv := server.NewServer(ctx, server.ArgoCDServerOpts{
EnableGZip: false,
Namespace: namespace,
@@ -244,7 +243,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, cache.RedisCompressionNone)
err := StartLocalServer(ctx, opts, ctxStr, nil, nil)
if err != nil {
log.Fatal(err)
}

View File

@@ -12,7 +12,7 @@ import (
"strings"
"time"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/coreos/go-oidc"
"github.com/golang-jwt/jwt/v4"
log "github.com/sirupsen/logrus"
"github.com/skratchdot/open-golang/open"

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"os"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/coreos/go-oidc"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"

View File

@@ -3,7 +3,6 @@ package commands
import (
"fmt"
"os"
"strconv"
"text/tabwriter"
log "github.com/sirupsen/logrus"
@@ -251,12 +250,15 @@ func printRepoTable(repos appsv1.Repositories) {
_, _ = fmt.Fprintf(w, "TYPE\tNAME\tREPO\tINSECURE\tOCI\tLFS\tCREDS\tSTATUS\tMESSAGE\tPROJECT\n")
for _, r := range repos {
var hasCreds string
if r.InheritedCreds {
hasCreds = "inherited"
if !r.HasCredentials() {
hasCreds = "false"
} else {
hasCreds = strconv.FormatBool(r.HasCredentials())
if r.InheritedCreds {
hasCreds = "inherited"
} else {
hasCreds = "true"
}
}
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%v\t%v\t%v\t%s\t%s\t%s\t%s\n", r.Type, r.Name, r.Repo, r.IsInsecure(), r.EnableOCI, r.EnableLFS, hasCreds, r.ConnectionState.Status, r.ConnectionState.Message, r.Project)
}
_ = w.Flush()
@@ -309,7 +311,7 @@ func NewRepoListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
},
}
command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|url")
command.Flags().StringVar(&refresh, "refresh", "", "Force a cache refresh on connection status , must be one of: 'hard'")
command.Flags().StringVar(&refresh, "refresh", "", "Force a cache refresh on connection status")
return command
}
@@ -360,6 +362,6 @@ func NewRepoGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
},
}
command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|url")
command.Flags().StringVar(&refresh, "refresh", "", "Force a cache refresh on connection status , must be one of: 'hard'")
command.Flags().StringVar(&refresh, "refresh", "", "Force a cache refresh on connection status")
return command
}

View File

@@ -64,13 +64,11 @@ type AppOptions struct {
jsonnetExtVarCode []string
jsonnetLibs []string
kustomizeImages []string
kustomizeReplicas []string
kustomizeVersion string
kustomizeCommonLabels []string
kustomizeCommonAnnotations []string
kustomizeForceCommonLabels bool
kustomizeForceCommonAnnotations bool
kustomizeNamespace string
pluginEnvs []string
Validate bool
directoryExclude string
@@ -119,14 +117,12 @@ func AddAppFlags(command *cobra.Command, opts *AppOptions) {
command.Flags().StringArrayVar(&opts.jsonnetExtVarCode, "jsonnet-ext-var-code", []string{}, "Jsonnet ext var")
command.Flags().StringArrayVar(&opts.jsonnetLibs, "jsonnet-libs", []string{}, "Additional jsonnet libs (prefixed by repoRoot)")
command.Flags().StringArrayVar(&opts.kustomizeImages, "kustomize-image", []string{}, "Kustomize images (e.g. --kustomize-image node:8.15.0 --kustomize-image mysql=mariadb,alpine@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d)")
command.Flags().StringArrayVar(&opts.kustomizeReplicas, "kustomize-replica", []string{}, "Kustomize replicas (e.g. --kustomize-replica my-development=2 --kustomize-replica my-statefulset=4)")
command.Flags().StringArrayVar(&opts.pluginEnvs, "plugin-env", []string{}, "Additional plugin envs")
command.Flags().BoolVar(&opts.Validate, "validate", true, "Validation of repo and cluster")
command.Flags().StringArrayVar(&opts.kustomizeCommonLabels, "kustomize-common-label", []string{}, "Set common labels in Kustomize")
command.Flags().StringArrayVar(&opts.kustomizeCommonAnnotations, "kustomize-common-annotation", []string{}, "Set common labels in Kustomize")
command.Flags().BoolVar(&opts.kustomizeForceCommonLabels, "kustomize-force-common-label", false, "Force common labels in Kustomize")
command.Flags().BoolVar(&opts.kustomizeForceCommonAnnotations, "kustomize-force-common-annotation", false, "Force common annotations in Kustomize")
command.Flags().StringVar(&opts.kustomizeNamespace, "kustomize-namespace", "", "Kustomize namespace")
command.Flags().StringVar(&opts.directoryExclude, "directory-exclude", "", "Set glob expression used to exclude files from application source path")
command.Flags().StringVar(&opts.directoryInclude, "directory-include", "", "Set glob expression used to include files from application source path")
command.Flags().Int64Var(&opts.retryLimit, "sync-retry-limit", 0, "Max number of allowed sync retries")
@@ -222,12 +218,8 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
setKustomizeOpt(source, kustomizeOpts{nameSuffix: appOpts.nameSuffix})
case "kustomize-image":
setKustomizeOpt(source, kustomizeOpts{images: appOpts.kustomizeImages})
case "kustomize-replica":
setKustomizeOpt(source, kustomizeOpts{replicas: appOpts.kustomizeReplicas})
case "kustomize-version":
setKustomizeOpt(source, kustomizeOpts{version: appOpts.kustomizeVersion})
case "kustomize-namespace":
setKustomizeOpt(source, kustomizeOpts{namespace: appOpts.kustomizeNamespace})
case "kustomize-common-label":
parsedLabels, err := label.Parse(appOpts.kustomizeCommonLabels)
errors.CheckError(err)
@@ -336,13 +328,11 @@ type kustomizeOpts struct {
namePrefix string
nameSuffix string
images []string
replicas []string
version string
commonLabels map[string]string
commonAnnotations map[string]string
forceCommonLabels bool
forceCommonAnnotations bool
namespace string
}
func setKustomizeOpt(src *argoappv1.ApplicationSource, opts kustomizeOpts) {
@@ -358,9 +348,6 @@ func setKustomizeOpt(src *argoappv1.ApplicationSource, opts kustomizeOpts) {
if opts.nameSuffix != "" {
src.Kustomize.NameSuffix = opts.nameSuffix
}
if opts.namespace != "" {
src.Kustomize.Namespace = opts.namespace
}
if opts.commonLabels != nil {
src.Kustomize.CommonLabels = opts.commonLabels
}
@@ -376,14 +363,6 @@ func setKustomizeOpt(src *argoappv1.ApplicationSource, opts kustomizeOpts) {
for _, image := range opts.images {
src.Kustomize.MergeImage(argoappv1.KustomizeImage(image))
}
for _, replica := range opts.replicas {
r, err := argoappv1.NewKustomizeReplica(replica)
if err != nil {
log.Fatal(err)
}
src.Kustomize.MergeReplica(*r)
}
if src.Kustomize.IsZero() {
src.Kustomize = nil
}

View File

@@ -10,8 +10,6 @@ import (
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"k8s.io/apimachinery/pkg/util/intstr"
)
func Test_setHelmOpt(t *testing.T) {
@@ -88,32 +86,11 @@ func Test_setKustomizeOpt(t *testing.T) {
setKustomizeOpt(&src, kustomizeOpts{images: []string{"org/image:v1", "org/image:v2"}})
assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{Images: v1alpha1.KustomizeImages{v1alpha1.KustomizeImage("org/image:v2")}}, src.Kustomize)
})
t.Run("Replicas", func(t *testing.T) {
src := v1alpha1.ApplicationSource{}
testReplicasString := []string{"my-deployment=2", "my-statefulset=4"}
testReplicas := v1alpha1.KustomizeReplicas{
{
Name: "my-deployment",
Count: intstr.FromInt(2),
},
{
Name: "my-statefulset",
Count: intstr.FromInt(4),
},
}
setKustomizeOpt(&src, kustomizeOpts{replicas: testReplicasString})
assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{Replicas: testReplicas}, src.Kustomize)
})
t.Run("Version", func(t *testing.T) {
src := v1alpha1.ApplicationSource{}
setKustomizeOpt(&src, kustomizeOpts{version: "v0.1"})
assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{Version: "v0.1"}, src.Kustomize)
})
t.Run("Namespace", func(t *testing.T) {
src := v1alpha1.ApplicationSource{}
setKustomizeOpt(&src, kustomizeOpts{namespace: "custom-namespace"})
assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{Namespace: "custom-namespace"}, src.Kustomize)
})
t.Run("Common labels", func(t *testing.T) {
src := v1alpha1.ApplicationSource{}
setKustomizeOpt(&src, kustomizeOpts{commonLabels: map[string]string{"foo1": "bar1", "foo2": "bar2"}})
@@ -214,11 +191,6 @@ func Test_setAppSpecOptions(t *testing.T) {
assert.NoError(t, f.SetFlag("sync-retry-limit", "0"))
assert.Nil(t, f.spec.SyncPolicy.Retry)
})
t.Run("Kustomize", func(t *testing.T) {
assert.NoError(t, f.SetFlag("kustomize-replica", "my-deployment=2"))
assert.NoError(t, f.SetFlag("kustomize-replica", "my-statefulset=4"))
assert.Equal(t, f.spec.Source.Kustomize.Replicas, argoappv1.KustomizeReplicas{{Name: "my-deployment", Count: intstr.FromInt(2)}, {Name: "my-statefulset", Count: intstr.FromInt(4)}})
})
}
func Test_setAnnotations(t *testing.T) {

View File

@@ -1,33 +1,20 @@
package util
import (
"context"
"fmt"
"os"
"sort"
"strings"
"text/tabwriter"
"github.com/ghodss/yaml"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
clientcmdapiv1 "k8s.io/client-go/tools/clientcmd/api/v1"
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/errors"
)
type ClusterEndpoint string
const (
KubeConfigEndpoint ClusterEndpoint = "kubeconfig"
KubePublicEndpoint ClusterEndpoint = "kube-public"
KubeInternalEndpoint ClusterEndpoint = "internal"
)
func PrintKubeContexts(ca clientcmd.ConfigAccess) {
config, err := ca.GetStartingConfig()
errors.CheckError(err)
@@ -115,30 +102,6 @@ func NewCluster(name string, namespaces []string, clusterResources bool, conf *r
return &clst
}
// GetKubePublicEndpoint returns the kubernetes apiserver endpoint as published
// in the kube-public.
func GetKubePublicEndpoint(client kubernetes.Interface) (string, error) {
clusterInfo, err := client.CoreV1().ConfigMaps("kube-public").Get(context.TODO(), "cluster-info", metav1.GetOptions{})
if err != nil {
return "", err
}
kubeconfig, ok := clusterInfo.Data["kubeconfig"]
if !ok {
return "", fmt.Errorf("cluster-info does not contain a public kubeconfig")
}
// Parse Kubeconfig and get server address
config := &clientcmdapiv1.Config{}
err = yaml.Unmarshal([]byte(kubeconfig), config)
if err != nil {
return "", fmt.Errorf("failed to parse cluster-info kubeconfig: %v", err)
}
if len(config.Clusters) == 0 {
return "", fmt.Errorf("cluster-info kubeconfig does not have any clusters")
}
return config.Clusters[0].Cluster.Server, nil
}
type ClusterOptions struct {
InCluster bool
Upsert bool
@@ -156,13 +119,6 @@ type ClusterOptions struct {
ExecProviderEnv map[string]string
ExecProviderAPIVersion string
ExecProviderInstallHint string
ClusterEndpoint string
}
// InClusterEndpoint returns true if ArgoCD should reference the in-cluster
// endpoint when registering the target cluster.
func (o ClusterOptions) InClusterEndpoint() bool {
return o.InCluster || o.ClusterEndpoint == string(KubeInternalEndpoint)
}
func AddClusterFlags(command *cobra.Command, opts *ClusterOptions) {
@@ -179,5 +135,4 @@ func AddClusterFlags(command *cobra.Command, opts *ClusterOptions) {
command.Flags().StringToStringVar(&opts.ExecProviderEnv, "exec-command-env", nil, "Environment vars to set when running the --exec-command executable")
command.Flags().StringVar(&opts.ExecProviderAPIVersion, "exec-command-api-version", "", "Preferred input version of the ExecInfo for the --exec-command executable")
command.Flags().StringVar(&opts.ExecProviderInstallHint, "exec-command-install-hint", "", "Text shown to the user when the --exec-command executable doesn't seem to be present")
command.Flags().StringVar(&opts.ClusterEndpoint, "cluster-endpoint", "", "Cluster endpoint to use. Can be one of the following: 'kubeconfig', 'kube-public', or 'internal'.")
}

View File

@@ -4,14 +4,8 @@ import (
"strings"
"testing"
"github.com/ghodss/yaml"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/rest"
clientcmdapiv1 "k8s.io/client-go/tools/clientcmd/api/v1"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
)
@@ -75,109 +69,3 @@ func Test_newCluster(t *testing.T) {
assert.Nil(t, clusterWithBearerToken.Labels)
assert.Nil(t, clusterWithBearerToken.Annotations)
}
func TestGetKubePublicEndpoint(t *testing.T) {
cases := []struct {
name string
clusterInfo *corev1.ConfigMap
expectedEndpoint string
expectError bool
}{
{
name: "has public endpoint",
clusterInfo: &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: "kube-public",
Name: "cluster-info",
},
Data: map[string]string{
"kubeconfig": kubeconfigFixture("https://test-cluster:6443"),
},
},
expectedEndpoint: "https://test-cluster:6443",
},
{
name: "no cluster-info",
expectError: true,
},
{
name: "no kubeconfig in cluster-info",
clusterInfo: &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: "kube-public",
Name: "cluster-info",
},
Data: map[string]string{
"argo": "the project, not the movie",
},
},
expectError: true,
},
{
name: "no clusters in cluster-info kubeconfig",
clusterInfo: &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: "kube-public",
Name: "cluster-info",
},
Data: map[string]string{
"kubeconfig": kubeconfigFixture(""),
},
},
expectError: true,
},
{
name: "can't parse kubeconfig",
clusterInfo: &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: "kube-public",
Name: "cluster-info",
},
Data: map[string]string{
"kubeconfig": "this is not valid YAML",
},
},
expectError: true,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
objects := []runtime.Object{}
if tc.clusterInfo != nil {
objects = append(objects, tc.clusterInfo)
}
clientset := fake.NewSimpleClientset(objects...)
endpoint, err := GetKubePublicEndpoint(clientset)
if err != nil && !tc.expectError {
t.Fatalf("unexpected error: %v", err)
}
if err == nil && tc.expectError {
t.Error("expected error to be returned, received none")
}
if endpoint != tc.expectedEndpoint {
t.Errorf("expected endpoint %s, got %s", tc.expectedEndpoint, endpoint)
}
})
}
}
func kubeconfigFixture(endpoint string) string {
kubeconfig := &clientcmdapiv1.Config{}
if len(endpoint) > 0 {
kubeconfig.Clusters = []clientcmdapiv1.NamedCluster{
{
Name: "test-kube",
Cluster: clientcmdapiv1.Cluster{
Server: endpoint,
},
},
}
}
configYAML, err := yaml.Marshal(kubeconfig)
if err != nil {
return ""
}
return string(configYAML)
}

View File

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

View File

@@ -22,24 +22,19 @@ type PluginConfig struct {
}
type PluginConfigSpec struct {
Version string `json:"version"`
Init Command `json:"init,omitempty"`
Generate Command `json:"generate"`
Discover Discover `json:"discover"`
Parameters Parameters `yaml:"parameters"`
PreserveFileMode bool `json:"preserveFileMode,omitempty"`
Version string `json:"version"`
Init Command `json:"init,omitempty"`
Generate Command `json:"generate"`
Discover Discover `json:"discover"`
Parameters Parameters `yaml:"parameters"`
}
// Discover holds find and fileName
//Discover holds find and fileName
type Discover struct {
Find Find `json:"find"`
FileName string `json:"fileName"`
}
func (d Discover) IsDefined() bool {
return d.FileName != "" || d.Find.Glob != "" || len(d.Find.Command.Command) > 0
}
// Command holds binary path and arguments list
type Command struct {
Command []string `json:"command,omitempty"`

View File

@@ -9,10 +9,8 @@ import (
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"time"
"unicode"
"github.com/argoproj/pkg/rand"
@@ -24,7 +22,6 @@ import (
"github.com/argoproj/argo-cd/v2/util/io/files"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/cyphar/filepath-securejoin"
"github.com/mattn/go-zglob"
log "github.com/sirupsen/logrus"
)
@@ -76,8 +73,9 @@ func runCommand(ctx context.Context, command Command, path string, env []string)
}
logCtx := log.WithFields(log.Fields{"execID": execId})
argsToLog := getCommandArgsToLog(cmd)
logCtx.WithFields(log.Fields{"dir": cmd.Dir}).Info(argsToLog)
// 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)
var stdout bytes.Buffer
var stderr bytes.Buffer
@@ -97,14 +95,6 @@ func runCommand(ctx context.Context, command Command, path string, env []string)
<-ctx.Done()
// Kill by group ID to make sure child processes are killed. The - tells `kill` that it's a group ID.
// Since we didn't set Pgid in SysProcAttr, the group ID is the same as the process ID. https://pkg.go.dev/syscall#SysProcAttr
// Sending a TERM signal first to allow any potential cleanup if needed, and then sending a KILL signal
_ = sysCallTerm(-cmd.Process.Pid)
// modify cleanup timeout to allow process to cleanup
cleanupTimeout := 5 * time.Second
time.Sleep(cleanupTimeout)
_ = sysCallKill(-cmd.Process.Pid)
}()
@@ -116,42 +106,14 @@ func runCommand(ctx context.Context, command Command, path string, env []string)
logCtx.WithFields(log.Fields{"duration": duration}).Debug(output)
if err != nil {
err := newCmdError(argsToLog, errors.New(err.Error()), strings.TrimSpace(stderr.String()))
err := newCmdError(args, errors.New(err.Error()), strings.TrimSpace(stderr.String()))
logCtx.Error(err.Error())
return strings.TrimSuffix(output, "\n"), err
}
if len(output) == 0 {
log.WithFields(log.Fields{
"stderr": stderr.String(),
"command": command,
}).Warn("Plugin command returned zero output")
}
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
@@ -191,7 +153,7 @@ func getTempDirMustCleanup(baseDir string) (workDir string, cleanup func(), err
if err := os.RemoveAll(workDir); err != nil {
log.WithFields(map[string]interface{}{
common.SecurityField: common.SecurityHigh,
common.SecurityCWEField: common.SecurityCWEIncompleteCleanup,
common.SecurityCWEField: 459,
}).Errorf("Failed to clean up temp directory: %s", err)
}
}
@@ -222,7 +184,7 @@ func (s *Service) generateManifestGeneric(stream GenerateManifestStream) error {
}
defer cleanup()
metadata, err := cmp.ReceiveRepoStream(ctx, stream, workDir, s.initConstants.PluginConfig.Spec.PreserveFileMode)
metadata, err := cmp.ReceiveRepoStream(ctx, stream, workDir)
if err != nil {
return fmt.Errorf("generate manifest error receiving stream: %w", err)
}
@@ -306,12 +268,12 @@ func (s *Service) matchRepositoryGeneric(stream MatchRepositoryStream) error {
}
defer cleanup()
metadata, err := cmp.ReceiveRepoStream(bufferedCtx, stream, workDir, s.initConstants.PluginConfig.Spec.PreserveFileMode)
metadata, err := cmp.ReceiveRepoStream(bufferedCtx, stream, workDir)
if err != nil {
return fmt.Errorf("match repository error receiving stream: %w", err)
}
isSupported, isDiscoveryEnabled, err := s.matchRepository(bufferedCtx, workDir, metadata.GetEnv(), metadata.GetAppRelPath())
isSupported, isDiscoveryEnabled, err := s.matchRepository(bufferedCtx, workDir, metadata.GetEnv())
if err != nil {
return fmt.Errorf("match repository error: %w", err)
}
@@ -324,20 +286,12 @@ func (s *Service) matchRepositoryGeneric(stream MatchRepositoryStream) error {
return nil
}
func (s *Service) matchRepository(ctx context.Context, workdir string, envEntries []*apiclient.EnvEntry, appRelPath string) (isSupported bool, isDiscoveryEnabled bool, err error) {
func (s *Service) matchRepository(ctx context.Context, workdir string, envEntries []*apiclient.EnvEntry) (isSupported bool, isDiscoveryEnabled bool, err error) {
config := s.initConstants.PluginConfig
appPath, err := securejoin.SecureJoin(workdir, appRelPath)
if err != nil {
log.WithFields(map[string]interface{}{
common.SecurityField: common.SecurityHigh,
common.SecurityCWEField: common.SecurityCWEIncompleteCleanup,
}).Errorf("error joining workdir %q and appRelPath %q: %v", workdir, appRelPath, err)
}
if config.Spec.Discover.FileName != "" {
log.Debugf("config.Spec.Discover.FileName is provided")
pattern := filepath.Join(appPath, config.Spec.Discover.FileName)
pattern := filepath.Join(workdir, config.Spec.Discover.FileName)
matches, err := filepath.Glob(pattern)
if err != nil {
e := fmt.Errorf("error finding filename match for pattern %q: %w", pattern, err)
@@ -349,7 +303,7 @@ func (s *Service) matchRepository(ctx context.Context, workdir string, envEntrie
if config.Spec.Discover.Find.Glob != "" {
log.Debugf("config.Spec.Discover.Find.Glob is provided")
pattern := filepath.Join(appPath, config.Spec.Discover.Find.Glob)
pattern := filepath.Join(workdir, config.Spec.Discover.Find.Glob)
// filepath.Glob doesn't have '**' support hence selecting third-party lib
// https://github.com/golang/go/issues/11862
matches, err := zglob.Glob(pattern)
@@ -365,7 +319,7 @@ func (s *Service) matchRepository(ctx context.Context, workdir string, envEntrie
if len(config.Spec.Discover.Find.Command.Command) > 0 {
log.Debugf("Going to try runCommand.")
env := append(os.Environ(), environ(envEntries)...)
find, err := runCommand(ctx, config.Spec.Discover.Find.Command, appPath, env)
find, err := runCommand(ctx, config.Spec.Discover.Find.Command, workdir, env)
if err != nil {
return false, true, fmt.Errorf("error running find command: %w", err)
}
@@ -392,7 +346,7 @@ func (s *Service) GetParametersAnnouncement(stream apiclient.ConfigManagementPlu
}
defer cleanup()
metadata, err := cmp.ReceiveRepoStream(bufferedCtx, stream, workDir, s.initConstants.PluginConfig.Spec.PreserveFileMode)
metadata, err := cmp.ReceiveRepoStream(bufferedCtx, stream, workDir)
if err != nil {
return fmt.Errorf("parameters announcement error receiving stream: %w", err)
}
@@ -401,7 +355,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, metadata.GetEnv())
repoResponse, err := getParametersAnnouncement(bufferedCtx, appPath, s.initConstants.PluginConfig.Spec.Parameters.Static, s.initConstants.PluginConfig.Spec.Parameters.Dynamic)
if err != nil {
return fmt.Errorf("get parameters announcement error: %w", err)
}
@@ -413,12 +367,11 @@ func (s *Service) GetParametersAnnouncement(stream apiclient.ConfigManagementPlu
return nil
}
func getParametersAnnouncement(ctx context.Context, appDir string, announcements []*repoclient.ParameterAnnouncement, command Command, envEntries []*apiclient.EnvEntry) (*apiclient.ParametersAnnouncementResponse, error) {
func getParametersAnnouncement(ctx context.Context, appDir string, announcements []*repoclient.ParameterAnnouncement, command Command) (*apiclient.ParametersAnnouncementResponse, error) {
augmentedAnnouncements := announcements
if len(command.Command) > 0 {
env := append(os.Environ(), environ(envEntries)...)
stdout, err := runCommand(ctx, command, appDir, env)
stdout, err := runCommand(ctx, command, appDir, os.Environ())
if err != nil {
return nil, fmt.Errorf("error executing dynamic parameter output command: %w", err)
}

View File

@@ -6,7 +6,6 @@ import (
"fmt"
"io"
"os"
"os/exec"
"path"
"path/filepath"
"testing"
@@ -100,7 +99,7 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".")
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.NoError(t, err)
@@ -115,7 +114,7 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".")
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.NoError(t, err)
@@ -130,7 +129,7 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
_, _, err := f.service.matchRepository(context.Background(), f.path, f.env, ".")
_, _, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.ErrorContains(t, err, "syntax error")
@@ -145,7 +144,7 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".")
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.NoError(t, err)
@@ -162,7 +161,7 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".")
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.NoError(t, err)
@@ -179,7 +178,7 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
_, _, err := f.service.matchRepository(context.Background(), f.path, f.env, ".")
_, _, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.ErrorContains(t, err, "error finding glob match for pattern")
@@ -196,7 +195,7 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".")
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.NoError(t, err)
@@ -215,7 +214,7 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".")
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.NoError(t, err)
assert.False(t, match)
@@ -233,7 +232,7 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".")
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.NoError(t, err)
@@ -253,7 +252,7 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".")
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.NoError(t, err)
@@ -272,7 +271,7 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".")
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.Error(t, err)
@@ -285,7 +284,7 @@ func TestMatchRepository(t *testing.T) {
f := setup(t, withDiscover(d))
// when
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".")
match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env)
// then
assert.NoError(t, err)
@@ -369,28 +368,6 @@ func TestRunCommandEmptyCommand(t *testing.T) {
assert.ErrorContains(t, err, "Command is empty")
}
// TestRunCommandContextTimeoutWithGracefulTermination makes sure that the process is given enough time to cleanup before sending SIGKILL.
func TestRunCommandContextTimeoutWithCleanup(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 900*time.Millisecond)
defer cancel()
// Use a subshell so there's a child command.
// This command sleeps for 4 seconds which is currently less than the 5 second delay between SIGTERM and SIGKILL signal and then exits successfully.
command := Command{
Command: []string{"sh", "-c"},
Args: []string{`(trap 'echo "cleanup completed"; exit' TERM; sleep 4)`},
}
before := time.Now()
output, err := runCommand(ctx, command, "", []string{})
after := time.Now()
assert.Error(t, err) // The command should time out, causing an error.
assert.Less(t, after.Sub(before), 1*time.Second)
// The command should still have completed the cleanup after termination.
assert.Contains(t, output, "cleanup completed")
}
func Test_getParametersAnnouncement_empty_command(t *testing.T) {
staticYAML := `
- name: static-a
@@ -403,7 +380,7 @@ func Test_getParametersAnnouncement_empty_command(t *testing.T) {
Command: []string{"echo"},
Args: []string{`[]`},
}
res, err := getParametersAnnouncement(context.Background(), "", *static, command, []*apiclient.EnvEntry{})
res, err := getParametersAnnouncement(context.Background(), "", *static, command)
require.NoError(t, err)
assert.Equal(t, []*repoclient.ParameterAnnouncement{{Name: "static-a"}, {Name: "static-b"}}, res.ParameterAnnouncements)
}
@@ -417,7 +394,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, []*apiclient.EnvEntry{})
res, err := getParametersAnnouncement(context.Background(), "", *static, command)
require.NoError(t, err)
assert.Equal(t, []*repoclient.ParameterAnnouncement{{Name: "static-a"}, {Name: "static-b"}}, res.ParameterAnnouncements)
}
@@ -434,7 +411,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, []*apiclient.EnvEntry{})
res, err := getParametersAnnouncement(context.Background(), "", *static, command)
require.NoError(t, err)
expected := []*repoclient.ParameterAnnouncement{
{Name: "dynamic-a"},
@@ -450,7 +427,7 @@ func Test_getParametersAnnouncement_invalid_json(t *testing.T) {
Command: []string{"echo"},
Args: []string{`[`},
}
_, err := getParametersAnnouncement(context.Background(), "", []*repoclient.ParameterAnnouncement{}, command, []*apiclient.EnvEntry{})
_, err := getParametersAnnouncement(context.Background(), "", []*repoclient.ParameterAnnouncement{}, command)
assert.Error(t, err)
assert.Contains(t, err.Error(), "unexpected end of JSON input")
}
@@ -460,7 +437,7 @@ func Test_getParametersAnnouncement_bad_command(t *testing.T) {
Command: []string{"exit"},
Args: []string{"1"},
}
_, err := getParametersAnnouncement(context.Background(), "", []*repoclient.ParameterAnnouncement{}, command, []*apiclient.EnvEntry{})
_, err := getParametersAnnouncement(context.Background(), "", []*repoclient.ParameterAnnouncement{}, command)
assert.Error(t, err)
assert.Contains(t, err.Error(), "error executing dynamic parameter output command")
}
@@ -752,17 +729,16 @@ 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", []string{"MUST_BE_SET=yep"})
s, err := NewMockParametersAnnouncementStream("./testdata/kustomize", "./testdata/kustomize", nil)
require.NoError(t, err)
err = service.GetParametersAnnouncement(s)
require.NoError(t, err)
require.NotNil(t, s.response)
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])
require.Len(t, s.response.ParameterAnnouncements, 1)
assert.Equal(t, repoclient.ParameterAnnouncement{Name: "test-param", String_: "test-value"}, *s.response.ParameterAnnouncements[0])
})
t.Run("out of bounds app", func(t *testing.T) {
s, err := NewMockParametersAnnouncementStream("./testdata/kustomize", "./testdata/kustomize", []string{"MUST_BE_SET=yep"})
s, err := NewMockParametersAnnouncementStream("./testdata/kustomize", "./testdata/kustomize", nil)
require.NoError(t, err)
// set a malicious app path on the metadata
s.metadataRequest.Request.(*apiclient.AppStreamRequest_Metadata).Metadata.AppRelPath = "../out-of-bounds"
@@ -770,38 +746,4 @@ 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

@@ -14,7 +14,3 @@ func newSysProcAttr(setpgid bool) *syscall.SysProcAttr {
func sysCallKill(pid int) error {
return syscall.Kill(pid, syscall.SIGKILL)
}
func sysCallTerm(pid int) error {
return syscall.Kill(pid, syscall.SIGTERM)
}

View File

@@ -14,7 +14,3 @@ func newSysProcAttr(setpgid bool) *syscall.SysProcAttr {
func sysCallKill(pid int) error {
return nil
}
func sysCallTerm(pid int) error {
return nil
}

View File

@@ -5,15 +5,9 @@ metadata:
spec:
version: v1.0
init:
command: [sh, -c]
args:
- |
kustomize version
command: [kustomize, version]
generate:
command: [sh, -c]
args:
- |
kustomize build
command: [sh, -c, "kustomize build"]
discover:
find:
command: [sh, -c, find . -name kustomization.yaml]
@@ -22,12 +16,3 @@ 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

@@ -159,10 +159,6 @@ const (
// Ex: "http://grafana.example.com/d/yu5UH4MMz/deployments"
// Ex: "Go to Dashboard|http://grafana.example.com/d/yu5UH4MMz/deployments"
AnnotationKeyLinkPrefix = "link.argocd.argoproj.io/"
// AnnotationKeyAppSkipReconcile tells the Application to skip the Application controller reconcile.
// Skip reconcile when the value is "true" or any other string values that can be strconv.ParseBool() to be true.
AnnotationKeyAppSkipReconcile = "argocd.argoproj.io/skip-reconcile"
)
// Environment variables for tuning and debugging Argo CD
@@ -309,16 +305,13 @@ const (
// Security severity logging
const (
SecurityField = "security"
// SecurityCWEField is the logs field for the CWE associated with a log line. CWE stands for Common Weakness Enumeration. See https://cwe.mitre.org/
SecurityCWEField = "CWE"
SecurityCWEIncompleteCleanup = 459
SecurityCWEMissingReleaseOfFileDescriptor = 775
SecurityEmergency = 5 // Indicates unmistakably malicious events that should NEVER occur accidentally and indicates an active attack (i.e. brute forcing, DoS)
SecurityCritical = 4 // Indicates any malicious or exploitable event that had a side effect (i.e. secrets being left behind on the filesystem)
SecurityHigh = 3 // Indicates likely malicious events but one that had no side effects or was blocked (i.e. out of bounds symlinks in repos)
SecurityMedium = 2 // Could indicate malicious events, but has a high likelihood of being user/system error (i.e. access denied)
SecurityLow = 1 // Unexceptional entries (i.e. successful access logs)
SecurityField = "security"
SecurityCWEField = "CWE"
SecurityEmergency = 5 // Indicates unmistakably malicious events that should NEVER occur accidentally and indicates an active attack (i.e. brute forcing, DoS)
SecurityCritical = 4 // Indicates any malicious or exploitable event that had a side effect (i.e. secrets being left behind on the filesystem)
SecurityHigh = 3 // Indicates likely malicious events but one that had no side effects or was blocked (i.e. out of bounds symlinks in repos)
SecurityMedium = 2 // Could indicate malicious events, but has a high likelihood of being user/system error (i.e. access denied)
SecurityLow = 1 // Unexceptional entries (i.e. successful access logs)
)
// Common error messages

View File

@@ -18,7 +18,6 @@ import (
"github.com/argoproj/gitops-engine/pkg/diff"
"github.com/argoproj/gitops-engine/pkg/health"
synccommon "github.com/argoproj/gitops-engine/pkg/sync/common"
resourceutil "github.com/argoproj/gitops-engine/pkg/sync/resource"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
jsonpatch "github.com/evanphx/json-patch"
log "github.com/sirupsen/logrus"
@@ -38,7 +37,6 @@ import (
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
"github.com/argoproj/argo-cd/v2/common"
statecache "github.com/argoproj/argo-cd/v2/controller/cache"
"github.com/argoproj/argo-cd/v2/controller/metrics"
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
@@ -53,7 +51,6 @@ import (
"github.com/argoproj/argo-cd/v2/util/db"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/glob"
"github.com/argoproj/argo-cd/v2/util/helm"
logutils "github.com/argoproj/argo-cd/v2/util/log"
settings_util "github.com/argoproj/argo-cd/v2/util/settings"
)
@@ -420,7 +417,7 @@ func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managed
nodes := make([]appv1.ResourceNode, 0)
proj, err := ctrl.getAppProj(a)
if err != nil {
return nil, fmt.Errorf("failed to get project: %w", err)
return nil, err
}
orphanedNodesMap := make(map[kube.ResourceKey]appv1.ResourceNode)
@@ -428,7 +425,7 @@ func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managed
if proj.Spec.OrphanedResources != nil {
orphanedNodesMap, err = ctrl.stateCache.GetNamespaceTopLevelResources(a.Spec.Destination.Server, a.Spec.Destination.Namespace)
if err != nil {
return nil, fmt.Errorf("failed to get namespace top-level resources: %w", err)
return nil, err
}
warnOrphaned = proj.Spec.OrphanedResources.IsWarn()
}
@@ -438,12 +435,12 @@ func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managed
var live = &unstructured.Unstructured{}
err := json.Unmarshal([]byte(managedResource.LiveState), &live)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal live state of managed resources: %w", err)
return nil, err
}
var target = &unstructured.Unstructured{}
err = json.Unmarshal([]byte(managedResource.TargetState), &target)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal target state of managed resources: %w", err)
return nil, err
}
if live == nil {
@@ -459,11 +456,7 @@ func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managed
} else {
err := ctrl.stateCache.IterateHierarchy(a.Spec.Destination.Server, kube.GetResourceKey(live), func(child appv1.ResourceNode, appName string) bool {
permitted, _ := proj.IsResourcePermitted(schema.GroupKind{Group: child.ResourceRef.Group, Kind: child.ResourceRef.Kind}, child.Namespace, a.Spec.Destination, func(project string) ([]*appv1.Cluster, error) {
clusters, err := ctrl.db.GetProjectClusters(context.TODO(), project)
if err != nil {
return nil, fmt.Errorf("failed to get project clusters: %w", err)
}
return clusters, nil
return ctrl.db.GetProjectClusters(context.TODO(), project)
})
if !permitted {
return false
@@ -472,7 +465,7 @@ func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managed
return true
})
if err != nil {
return nil, fmt.Errorf("failed to iterate resource hierarchy: %w", err)
return nil, err
}
}
}
@@ -521,7 +514,7 @@ func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managed
hosts, err := ctrl.getAppHosts(a, nodes)
if err != nil {
return nil, fmt.Errorf("failed to get app hosts: %w", err)
return nil, err
}
return &appv1.ApplicationTree{Nodes: nodes, OrphanedNodes: orphanedNodes, Hosts: hosts}, nil
}
@@ -840,7 +833,7 @@ func (ctrl *ApplicationController) processAppOperationQueueItem() (processNext b
Message: err.Error(),
})
message := fmt.Sprintf("Unable to delete application resources: %v", err.Error())
ctrl.auditLogger.LogAppEvent(app, argo.EventInfo{Reason: argo.EventReasonStatusRefreshed, Type: v1.EventTypeWarning}, message, "")
ctrl.auditLogger.LogAppEvent(app, argo.EventInfo{Reason: argo.EventReasonStatusRefreshed, Type: v1.EventTypeWarning}, message)
}
}
return
@@ -944,9 +937,7 @@ func (ctrl *ApplicationController) removeProjectFinalizer(proj *appv1.AppProject
// shouldBeDeleted returns whether a given resource obj should be deleted on cascade delete of application app
func (ctrl *ApplicationController) shouldBeDeleted(app *appv1.Application, obj *unstructured.Unstructured) bool {
return !kube.IsCRD(obj) && !isSelfReferencedApp(app, kube.GetObjectRef(obj)) &&
!resourceutil.HasAnnotationOption(obj, synccommon.AnnotationSyncOptions, synccommon.SyncOptionDisableDeletion) &&
!resourceutil.HasAnnotationOption(obj, helm.ResourcePolicyAnnotation, helm.ResourcePolicyKeep)
return !kube.IsCRD(obj) && !isSelfReferencedApp(app, kube.GetObjectRef(obj))
}
func (ctrl *ApplicationController) getPermittedAppLiveObjects(app *appv1.Application, proj *appv1.AppProject, projectClusters func(project string) ([]*appv1.Cluster, error)) (map[kube.ResourceKey]*unstructured.Unstructured, error) {
@@ -1240,44 +1231,40 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli
}
func (ctrl *ApplicationController) setOperationState(app *appv1.Application, state *appv1.OperationState) {
logCtx := log.WithFields(log.Fields{"application": app.Name, "appNamespace": app.Namespace, "project": app.Spec.Project})
if state.Phase == "" {
// expose any bugs where we neglect to set phase
panic("no phase was set")
}
if state.Phase.Completed() {
now := metav1.Now()
state.FinishedAt = &now
}
patch := map[string]interface{}{
"status": map[string]interface{}{
"operationState": state,
},
}
if state.Phase.Completed() {
// If operation is completed, clear the operation field to indicate no operation is
// in progress.
patch["operation"] = nil
}
if reflect.DeepEqual(app.Status.OperationState, state) {
logCtx.Infof("No operation updates necessary to '%s'. Skipping patch", app.QualifiedName())
return
}
patchJSON, err := json.Marshal(patch)
if err != nil {
logCtx.Errorf("error marshaling json: %v", err)
return
}
if app.Status.OperationState != nil && app.Status.OperationState.FinishedAt != nil && state.FinishedAt == nil {
patchJSON, err = jsonpatch.MergeMergePatches(patchJSON, []byte(`{"status": {"operationState": {"finishedAt": null}}}`))
if err != nil {
logCtx.Errorf("error merging operation state patch: %v", err)
return
}
}
kube.RetryUntilSucceed(context.Background(), updateOperationStateTimeout, "Update application operation state", logutils.NewLogrusLogger(logutils.NewWithCurrentConfig()), func() error {
if state.Phase == "" {
// expose any bugs where we neglect to set phase
panic("no phase was set")
}
if state.Phase.Completed() {
now := metav1.Now()
state.FinishedAt = &now
}
patch := map[string]interface{}{
"status": map[string]interface{}{
"operationState": state,
},
}
if state.Phase.Completed() {
// If operation is completed, clear the operation field to indicate no operation is
// in progress.
patch["operation"] = nil
}
if reflect.DeepEqual(app.Status.OperationState, state) {
log.Infof("No operation updates necessary to '%s'. Skipping patch", app.QualifiedName())
return nil
}
patchJSON, err := json.Marshal(patch)
if err != nil {
return fmt.Errorf("error marshaling json: %w", err)
}
if app.Status.OperationState != nil && app.Status.OperationState.FinishedAt != nil && state.FinishedAt == nil {
patchJSON, err = jsonpatch.MergeMergePatches(patchJSON, []byte(`{"status": {"operationState": {"finishedAt": null}}}`))
if err != nil {
return fmt.Errorf("error merging operation state patch: %w", err)
}
}
appClient := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace)
_, err = appClient.Patch(context.Background(), app.Name, types.MergePatchType, patchJSON, metav1.PatchOptions{})
if err != nil {
@@ -1285,36 +1272,32 @@ func (ctrl *ApplicationController) setOperationState(app *appv1.Application, sta
if apierr.IsNotFound(err) {
return nil
}
// kube.RetryUntilSucceed logs failed attempts at "debug" level, but we want to know if this fails. Log a
// warning.
logCtx.Warnf("error patching application with operation state: %v", err)
return fmt.Errorf("error patching application with operation state: %w", err)
}
log.Infof("updated '%s' operation (phase: %s)", app.QualifiedName(), state.Phase)
if state.Phase.Completed() {
eventInfo := argo.EventInfo{Reason: argo.EventReasonOperationCompleted}
var messages []string
if state.Operation.Sync != nil && len(state.Operation.Sync.Resources) > 0 {
messages = []string{"Partial sync operation"}
} else {
messages = []string{"Sync operation"}
}
if state.SyncResult != nil {
messages = append(messages, "to", state.SyncResult.Revision)
}
if state.Phase.Successful() {
eventInfo.Type = v1.EventTypeNormal
messages = append(messages, "succeeded")
} else {
eventInfo.Type = v1.EventTypeWarning
messages = append(messages, "failed:", state.Message)
}
ctrl.auditLogger.LogAppEvent(app, eventInfo, strings.Join(messages, " "))
ctrl.metricsServer.IncSync(app, state)
}
return nil
})
logCtx.Infof("updated '%s' operation (phase: %s)", app.QualifiedName(), state.Phase)
if state.Phase.Completed() {
eventInfo := argo.EventInfo{Reason: argo.EventReasonOperationCompleted}
var messages []string
if state.Operation.Sync != nil && len(state.Operation.Sync.Resources) > 0 {
messages = []string{"Partial sync operation"}
} else {
messages = []string{"Sync operation"}
}
if state.SyncResult != nil {
messages = append(messages, "to", state.SyncResult.Revision)
}
if state.Phase.Successful() {
eventInfo.Type = v1.EventTypeNormal
messages = append(messages, "succeeded")
} else {
eventInfo.Type = v1.EventTypeWarning
messages = append(messages, "failed:", state.Message)
}
ctrl.auditLogger.LogAppEvent(app, eventInfo, strings.Join(messages, " "), "")
ctrl.metricsServer.IncSync(app, state)
}
}
func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext bool) {
@@ -1373,7 +1356,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
} else {
var tree *appv1.ApplicationTree
if tree, err = ctrl.getResourceTree(app, managedResources); err == nil {
app.Status.Summary = tree.GetSummary(app)
app.Status.Summary = tree.GetSummary()
if err := ctrl.cache.SetAppResourcesTree(app.InstanceName(ctrl.namespace), tree); err != nil {
logCtx.Errorf("Failed to cache resources tree: %v", err)
return
@@ -1447,7 +1430,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
if err != nil {
logCtx.Errorf("Failed to cache app resources: %v", err)
} else {
app.Status.Summary = tree.GetSummary(app)
app.Status.Summary = tree.GetSummary()
}
if project.Spec.SyncWindows.Matches(app).CanSync(false) {
@@ -1490,7 +1473,7 @@ func currentSourceEqualsSyncedSource(app *appv1.Application) bool {
if app.Spec.HasMultipleSources() {
return app.Spec.Sources.Equals(app.Status.Sync.ComparedTo.Sources)
}
return app.Spec.Source.Equals(&app.Status.Sync.ComparedTo.Source)
return app.Spec.Source.Equals(app.Status.Sync.ComparedTo.Source)
}
// needRefreshAppStatus answers if application status needs to be refreshed.
@@ -1593,11 +1576,11 @@ func (ctrl *ApplicationController) persistAppStatus(orig *appv1.Application, new
logCtx := log.WithFields(log.Fields{"application": orig.QualifiedName()})
if orig.Status.Sync.Status != newStatus.Sync.Status {
message := fmt.Sprintf("Updated sync status: %s -> %s", orig.Status.Sync.Status, newStatus.Sync.Status)
ctrl.auditLogger.LogAppEvent(orig, argo.EventInfo{Reason: argo.EventReasonResourceUpdated, Type: v1.EventTypeNormal}, message, "")
ctrl.auditLogger.LogAppEvent(orig, argo.EventInfo{Reason: argo.EventReasonResourceUpdated, Type: v1.EventTypeNormal}, message)
}
if orig.Status.Health.Status != newStatus.Health.Status {
message := fmt.Sprintf("Updated health status: %s -> %s", orig.Status.Health.Status, newStatus.Health.Status)
ctrl.auditLogger.LogAppEvent(orig, argo.EventInfo{Reason: argo.EventReasonResourceUpdated, Type: v1.EventTypeNormal}, message, "")
ctrl.auditLogger.LogAppEvent(orig, argo.EventInfo{Reason: argo.EventReasonResourceUpdated, Type: v1.EventTypeNormal}, message)
}
var newAnnotations map[string]string
if orig.GetAnnotations() != nil {
@@ -1732,7 +1715,7 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *
return &appv1.ApplicationCondition{Type: appv1.ApplicationConditionSyncError, Message: err.Error()}
}
message := fmt.Sprintf("Initiated automated sync to '%s'", desiredCommitSHA)
ctrl.auditLogger.LogAppEvent(app, argo.EventInfo{Reason: argo.EventReasonOperationStarted, Type: v1.EventTypeNormal}, message, "")
ctrl.auditLogger.LogAppEvent(app, argo.EventInfo{Reason: argo.EventReasonOperationStarted, Type: v1.EventTypeNormal}, message)
logCtx.Info(message)
return nil
}
@@ -1802,20 +1785,6 @@ func (ctrl *ApplicationController) canProcessApp(obj interface{}) bool {
return false
}
if annotations := app.GetAnnotations(); annotations != nil {
if skipVal, ok := annotations[common.AnnotationKeyAppSkipReconcile]; ok {
logCtx := log.WithFields(log.Fields{"application": app.QualifiedName()})
if skipReconcile, err := strconv.ParseBool(skipVal); err == nil {
if skipReconcile {
logCtx.Debugf("Skipping Application reconcile based on annotation %s", common.AnnotationKeyAppSkipReconcile)
return false
}
} else {
logCtx.Debugf("Unable to determine if Application should skip reconcile based on annotation %s: %v", common.AnnotationKeyAppSkipReconcile, err)
}
}
}
if ctrl.clusterFilter != nil {
cluster, err := ctrl.db.GetCluster(context.Background(), app.Spec.Destination.Server)
if err != nil {

View File

@@ -3,11 +3,9 @@ package controller
import (
"context"
"encoding/json"
"errors"
"testing"
"time"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/api/resource"
clustercache "github.com/argoproj/gitops-engine/pkg/cache"
@@ -52,7 +50,6 @@ type namespacedResource struct {
type fakeData struct {
apps []runtime.Object
manifestResponse *apiclient.ManifestResponse
manifestResponses []*apiclient.ManifestResponse
managedLiveObjs map[kube.ResourceKey]*unstructured.Unstructured
namespacedResources map[kube.ResourceKey]namespacedResource
configMapData map[string]string
@@ -68,15 +65,7 @@ func newFakeController(data *fakeData) *ApplicationController {
// Mock out call to GenerateManifest
mockRepoClient := mockrepoclient.RepoServerServiceClient{}
if len(data.manifestResponses) > 0 {
for _, response := range data.manifestResponses {
mockRepoClient.On("GenerateManifest", mock.Anything, mock.Anything).Return(response, nil).Once()
}
} else {
mockRepoClient.On("GenerateManifest", mock.Anything, mock.Anything).Return(data.manifestResponse, nil)
}
mockRepoClient.On("GenerateManifest", mock.Anything, mock.Anything).Return(data.manifestResponse, nil)
mockRepoClientset := mockrepoclient.Clientset{RepoServerServiceClient: &mockRepoClient}
secret := corev1.Secret{
@@ -234,14 +223,9 @@ spec:
project: default
sources:
- path: some/path
helm:
valueFiles:
- $values_test/values.yaml
repoURL: https://github.com/argoproj/argocd-example-apps.git
- path: some/other/path
repoURL: https://github.com/argoproj/argocd-example-apps-fake.git
- ref: values_test
repoURL: https://github.com/argoproj/argocd-example-apps-fake-ref.git
syncPolicy:
automated: {}
status:
@@ -253,7 +237,6 @@ status:
revisions:
- HEAD
- HEAD
- HEAD
phase: Succeeded
startedAt: 2018-09-21T23:50:25Z
syncResult:
@@ -268,14 +251,11 @@ status:
revisions:
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
- bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
- cccccccccccccccccccccccccccccccccccccccc
sources:
- path: some/path
repoURL: https://github.com/argoproj/argocd-example-apps.git
- path: some/other/path
repoURL: https://github.com/argoproj/argocd-example-apps-fake.git
- path: some/other/path
repoURL: https://github.com/argoproj/argocd-example-apps-fake-ref.git
`
var fakeAppWithDestName = `
@@ -929,41 +909,6 @@ func TestSetOperationStateOnDeletedApp(t *testing.T) {
assert.True(t, patched)
}
type logHook struct {
entries []logrus.Entry
}
func (h *logHook) Levels() []logrus.Level {
return []logrus.Level{logrus.WarnLevel}
}
func (h *logHook) Fire(entry *logrus.Entry) error {
h.entries = append(h.entries, *entry)
return nil
}
func TestSetOperationStateLogRetries(t *testing.T) {
hook := logHook{}
logrus.AddHook(&hook)
t.Cleanup(func() {
logrus.StandardLogger().ReplaceHooks(logrus.LevelHooks{})
})
ctrl := newFakeController(&fakeData{apps: []runtime.Object{}})
fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset)
fakeAppCs.ReactionChain = nil
patched := false
fakeAppCs.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
if !patched {
patched = true
return true, nil, errors.New("fake error")
}
return true, nil, nil
})
ctrl.setOperationState(newFakeApp(), &v1alpha1.OperationState{Phase: synccommon.OperationSucceeded})
assert.True(t, patched)
assert.Contains(t, hook.entries[0].Message, "fake error")
}
func TestNeedRefreshAppStatus(t *testing.T) {
testCases := []struct {
name string
@@ -1541,52 +1486,3 @@ func Test_canProcessApp(t *testing.T) {
assert.False(t, canProcess)
})
}
func Test_canProcessAppSkipReconcileAnnotation(t *testing.T) {
appSkipReconcileInvalid := newFakeApp()
appSkipReconcileInvalid.Annotations = map[string]string{common.AnnotationKeyAppSkipReconcile: "invalid-value"}
appSkipReconcileFalse := newFakeApp()
appSkipReconcileFalse.Annotations = map[string]string{common.AnnotationKeyAppSkipReconcile: "false"}
appSkipReconcileTrue := newFakeApp()
appSkipReconcileTrue.Annotations = map[string]string{common.AnnotationKeyAppSkipReconcile: "true"}
ctrl := newFakeController(&fakeData{})
tests := []struct {
name string
input interface{}
expected bool
}{
{"No skip reconcile annotation", newFakeApp(), true},
{"Contains skip reconcile annotation ", appSkipReconcileInvalid, true},
{"Contains skip reconcile annotation value false", appSkipReconcileFalse, true},
{"Contains skip reconcile annotation value true", appSkipReconcileTrue, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.expected, ctrl.canProcessApp(tt.input))
})
}
}
func Test_syncDeleteOption(t *testing.T) {
app := newFakeApp()
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}})
cm := newFakeCM()
t.Run("without delete option object is deleted", func(t *testing.T) {
cmObj := kube.MustToUnstructured(&cm)
delete := ctrl.shouldBeDeleted(app, cmObj)
assert.True(t, delete)
})
t.Run("with delete set to false object is retained", func(t *testing.T) {
cmObj := kube.MustToUnstructured(&cm)
cmObj.SetAnnotations(map[string]string{"argocd.argoproj.io/sync-options": "Delete=false"})
delete := ctrl.shouldBeDeleted(app, cmObj)
assert.False(t, delete)
})
t.Run("with delete set to false object is retained", func(t *testing.T) {
cmObj := kube.MustToUnstructured(&cm)
cmObj.SetAnnotations(map[string]string{"helm.sh/resource-policy": "keep"})
delete := ctrl.shouldBeDeleted(app, cmObj)
assert.False(t, delete)
})
}

View File

@@ -221,10 +221,10 @@ func asResourceNode(r *clustercache.Resource) appv1.ResourceNode {
gv = schema.GroupVersion{}
}
parentRefs := make([]appv1.ResourceRef, len(r.OwnerRefs))
for i, ownerRef := range r.OwnerRefs {
for _, ownerRef := range r.OwnerRefs {
ownerGvk := schema.FromAPIVersionAndKind(ownerRef.APIVersion, ownerRef.Kind)
ownerKey := kube.NewResourceKey(ownerGvk.Group, ownerRef.Kind, r.Ref.Namespace, ownerRef.Name)
parentRefs[i] = appv1.ResourceRef{Name: ownerRef.Name, Kind: ownerKey.Kind, Namespace: r.Ref.Namespace, Group: ownerKey.Group, UID: string(ownerRef.UID)}
parentRefs[0] = appv1.ResourceRef{Name: ownerRef.Name, Kind: ownerKey.Kind, Namespace: r.Ref.Namespace, Group: ownerKey.Group, UID: string(ownerRef.UID)}
}
var resHealth *appv1.HealthStatus
resourceInfo := resInfo(r)
@@ -488,11 +488,10 @@ func (c *liveStateCache) getSyncedCluster(server string) (clustercache.ClusterCa
func (c *liveStateCache) invalidate(cacheSettings cacheSettings) {
log.Info("invalidating live state cache")
c.lock.Lock()
c.cacheSettings = cacheSettings
clusters := c.clusters
c.lock.Unlock()
defer c.lock.Unlock()
for _, clust := range clusters {
c.cacheSettings = cacheSettings
for _, clust := range c.clusters {
clust.Invalidate(clustercache.SetSettings(cacheSettings.clusterSettings))
}
log.Info("live state cache invalidated")
@@ -702,14 +701,12 @@ func (c *liveStateCache) handleModEvent(oldCluster *appv1.Cluster, newCluster *a
}
func (c *liveStateCache) handleDeleteEvent(clusterServer string) {
c.lock.RLock()
c.lock.Lock()
defer c.lock.Unlock()
cluster, ok := c.clusters[clusterServer]
c.lock.RUnlock()
if ok {
cluster.Invalidate()
c.lock.Lock()
delete(c.clusters, clusterServer)
c.lock.Unlock()
}
}

View File

@@ -1,27 +1,21 @@
package cache
import (
"context"
"errors"
"net"
"net/url"
"sync"
"testing"
"time"
apierr "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
apierr "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/argoproj/gitops-engine/pkg/cache"
"github.com/argoproj/gitops-engine/pkg/cache/mocks"
"github.com/stretchr/testify/mock"
"k8s.io/client-go/kubernetes/fake"
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
argosettings "github.com/argoproj/argo-cd/v2/util/settings"
)
type netError string
@@ -112,98 +106,6 @@ func TestHandleAddEvent_ClusterExcluded(t *testing.T) {
assert.Len(t, clustersCache.clusters, 0)
}
func TestHandleDeleteEvent_CacheDeadlock(t *testing.T) {
testCluster := &appv1.Cluster{
Server: "https://mycluster",
Config: appv1.ClusterConfig{Username: "bar"},
}
fakeClient := fake.NewSimpleClientset()
settingsMgr := argosettings.NewSettingsManager(context.TODO(), fakeClient, "argocd")
externalLockRef := sync.RWMutex{}
gitopsEngineClusterCache := &mocks.ClusterCache{}
clustersCache := liveStateCache{
clusters: map[string]cache.ClusterCache{
testCluster.Server: gitopsEngineClusterCache,
},
clusterFilter: func(cluster *appv1.Cluster) bool {
return true
},
settingsMgr: settingsMgr,
// Set the lock here so we can reference it later
// nolint We need to overwrite here to have access to the lock
lock: externalLockRef,
}
channel := make(chan string)
// Mocked lock held by the gitops-engine cluster cache
mockMutex := sync.RWMutex{}
// Locks to force trigger condition during test
// Condition order:
// EnsuredSynced -> Locks gitops-engine
// handleDeleteEvent -> Locks liveStateCache
// EnsureSynced via sync, newResource, populateResourceInfoHandler -> attempts to Lock liveStateCache
// handleDeleteEvent via cluster.Invalidate -> attempts to Lock gitops-engine
handleDeleteWasCalled := sync.Mutex{}
engineHoldsLock := sync.Mutex{}
handleDeleteWasCalled.Lock()
engineHoldsLock.Lock()
gitopsEngineClusterCache.On("EnsureSynced").Run(func(args mock.Arguments) {
// Held by EnsureSync calling into sync and watchEvents
mockMutex.Lock()
defer mockMutex.Unlock()
// Continue Execution of timer func
engineHoldsLock.Unlock()
// Wait for handleDeleteEvent to be called triggering the lock
// on the liveStateCache
handleDeleteWasCalled.Lock()
t.Logf("handleDelete was called, EnsureSynced continuing...")
handleDeleteWasCalled.Unlock()
// Try and obtain the lock on the liveStateCache
alreadyFailed := !externalLockRef.TryLock()
if alreadyFailed {
channel <- "DEADLOCKED -- EnsureSynced could not obtain lock on liveStateCache"
return
}
externalLockRef.Lock()
t.Logf("EnsureSynce was able to lock liveStateCache")
externalLockRef.Unlock()
}).Return(nil).Once()
gitopsEngineClusterCache.On("Invalidate").Run(func(args mock.Arguments) {
// If deadlock is fixed should be able to acquire lock here
alreadyFailed := !mockMutex.TryLock()
if alreadyFailed {
channel <- "DEADLOCKED -- Invalidate could not obtain lock on gitops-engine"
return
}
mockMutex.Lock()
t.Logf("Invalidate was able to lock gitops-engine cache")
mockMutex.Unlock()
}).Return()
go func() {
// Start the gitops-engine lock holds
go func() {
err := gitopsEngineClusterCache.EnsureSynced()
if err != nil {
assert.Fail(t, err.Error())
}
}()
// Wait for EnsureSynced to grab the lock for gitops-engine
engineHoldsLock.Lock()
t.Log("EnsureSynced has obtained lock on gitops-engine")
engineHoldsLock.Unlock()
// Run in background
go clustersCache.handleDeleteEvent(testCluster.Server)
// Allow execution to continue on clusters cache call to trigger lock
handleDeleteWasCalled.Unlock()
channel <- "PASSED"
}()
select {
case str := <-channel:
assert.Equal(t, "PASSED", str, str)
case <-time.After(5 * time.Second):
assert.Fail(t, "Ended up in deadlock")
}
}
func TestIsRetryableError(t *testing.T) {
var (
tlsHandshakeTimeoutErr net.Error = netError("net/http: TLS handshake timeout")
@@ -251,51 +153,3 @@ func TestIsRetryableError(t *testing.T) {
assert.True(t, isRetryableError(connectionReset))
})
}
func Test_asResourceNode_owner_refs(t *testing.T) {
resNode := asResourceNode(&cache.Resource{
ResourceVersion: "",
Ref: v1.ObjectReference{
APIVersion: "v1",
},
OwnerRefs: []metav1.OwnerReference{
{
APIVersion: "v1",
Kind: "ConfigMap",
Name: "cm-1",
},
{
APIVersion: "v1",
Kind: "ConfigMap",
Name: "cm-2",
},
},
CreationTimestamp: nil,
Info: nil,
Resource: nil,
})
expected := appv1.ResourceNode{
ResourceRef: appv1.ResourceRef{
Version: "v1",
},
ParentRefs: []appv1.ResourceRef{
{
Group: "",
Kind: "ConfigMap",
Name: "cm-1",
},
{
Group: "",
Kind: "ConfigMap",
Name: "cm-2",
},
},
Info: nil,
NetworkingInfo: nil,
ResourceVersion: "",
Images: nil,
Health: nil,
CreatedAt: nil,
}
assert.Equal(t, expected, resNode)
}

View File

@@ -2,13 +2,13 @@ package controller
import (
"context"
"time"
"fmt"
"github.com/argoproj/gitops-engine/pkg/cache"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
log "github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"time"
"github.com/argoproj/argo-cd/v2/controller/metrics"
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"

View File

@@ -1,13 +1,10 @@
package controller
import (
"fmt"
"github.com/argoproj/gitops-engine/pkg/health"
hookutil "github.com/argoproj/gitops-engine/pkg/sync/hook"
"github.com/argoproj/gitops-engine/pkg/sync/ignore"
kubeutil "github.com/argoproj/gitops-engine/pkg/utils/kube"
log "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
@@ -18,7 +15,6 @@ import (
// setApplicationHealth updates the health statuses of all resources performed in the comparison
func setApplicationHealth(resources []managedResource, statuses []appv1.ResourceStatus, resourceOverrides map[string]appv1.ResourceOverride, app *appv1.Application, persistResourceHealth bool) (*appv1.HealthStatus, error) {
var savedErr error
var errCount uint
appHealth := appv1.HealthStatus{Status: health.HealthStatusHealthy}
for i, res := range resources {
if res.Target != nil && hookutil.Skip(res.Target) {
@@ -42,10 +38,7 @@ func setApplicationHealth(resources []managedResource, statuses []appv1.Resource
}
healthStatus, err = health.GetResourceHealth(res.Live, healthOverrides)
if err != nil && savedErr == nil {
errCount++
savedErr = fmt.Errorf("failed to get resource health for %q with name %q in namespace %q: %w", res.Live.GetKind(), res.Live.GetName(), res.Live.GetNamespace(), err)
// also log so we don't lose the message
log.WithField("application", app.QualifiedName()).Warn(savedErr)
savedErr = err
}
}
@@ -79,8 +72,5 @@ func setApplicationHealth(resources []managedResource, statuses []appv1.Resource
} else {
app.Status.ResourceHealthSource = appv1.ResourceHealthLocationAppTree
}
if savedErr != nil && errCount > 1 {
savedErr = fmt.Errorf("see applicaton-controller logs for %d other errors; most recent error was: %w", errCount-1, savedErr)
}
return &appHealth, savedErr
}

View File

@@ -124,7 +124,7 @@ func newAppLiveObj(status health.HealthStatusCode) *unstructured.Unstructured {
},
TypeMeta: metav1.TypeMeta{
APIVersion: "argoproj.io/v1alpha1",
Kind: application.ApplicationKind,
Kind: "Application",
},
Status: appv1.ApplicationStatus{
Health: appv1.HealthStatus{

View File

@@ -207,7 +207,7 @@ func runTest(t *testing.T, cfg TestMetricServerConfig) {
metricsServ.registry.MustRegister(collector)
}
req, err := http.NewRequest(http.MethodGet, "/metrics", nil)
req, err := http.NewRequest("GET", "/metrics", nil)
assert.NoError(t, err)
rr := httptest.NewRecorder()
metricsServ.Handler.ServeHTTP(rr, req)
@@ -337,7 +337,7 @@ argocd_app_sync_total{dest_server="https://localhost:6443",name="my-app",namespa
metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: common.OperationSucceeded})
metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: common.OperationSucceeded})
req, err := http.NewRequest(http.MethodGet, "/metrics", nil)
req, err := http.NewRequest("GET", "/metrics", nil)
assert.NoError(t, err)
rr := httptest.NewRecorder()
metricsServ.Handler.ServeHTTP(rr, req)
@@ -391,7 +391,7 @@ argocd_app_reconcile_count{dest_server="https://localhost:6443",namespace="argoc
fakeApp := newFakeApp(fakeApp)
metricsServ.IncReconcile(fakeApp, 5*time.Second)
req, err := http.NewRequest(http.MethodGet, "/metrics", nil)
req, err := http.NewRequest("GET", "/metrics", nil)
assert.NoError(t, err)
rr := httptest.NewRecorder()
metricsServ.Handler.ServeHTTP(rr, req)
@@ -415,7 +415,7 @@ argocd_app_sync_total{dest_server="https://localhost:6443",name="my-app",namespa
argocd_app_sync_total{dest_server="https://localhost:6443",name="my-app",namespace="argocd",phase="Succeeded",project="important-project"} 2
`
req, err := http.NewRequest(http.MethodGet, "/metrics", nil)
req, err := http.NewRequest("GET", "/metrics", nil)
assert.NoError(t, err)
rr := httptest.NewRecorder()
metricsServ.Handler.ServeHTTP(rr, req)
@@ -426,7 +426,7 @@ argocd_app_sync_total{dest_server="https://localhost:6443",name="my-app",namespa
err = metricsServ.SetExpiration(time.Second)
assert.NoError(t, err)
time.Sleep(2 * time.Second)
req, err = http.NewRequest(http.MethodGet, "/metrics", nil)
req, err = http.NewRequest("GET", "/metrics", nil)
assert.NoError(t, err)
rr = httptest.NewRecorder()
metricsServ.Handler.ServeHTTP(rr, req)

View File

@@ -107,7 +107,7 @@ type appStateManager struct {
persistResourceHealth bool
}
func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, sources []v1alpha1.ApplicationSource, appLabelKey string, revisions []string, noCache, noRevisionCache, verifySignature bool, proj *v1alpha1.AppProject) ([]*unstructured.Unstructured, []*apiclient.ManifestResponse, error) {
func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, sources []v1alpha1.ApplicationSource, appLabelKey string, revisions []string, noCache, noRevisionCache, verifySignature bool, proj *v1alpha1.AppProject) ([]*unstructured.Unstructured, map[*v1alpha1.ApplicationSource]*apiclient.ManifestResponse, error) {
ts := stats.NewTimingStats()
helmRepos, err := m.db.ListHelmRepositories(context.Background())
@@ -164,7 +164,7 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, sources []v1alp
}
defer io.Close(conn)
manifestInfos := make([]*apiclient.ManifestResponse, 0)
manifestInfoMap := make(map[*v1alpha1.ApplicationSource]*apiclient.ManifestResponse)
targetObjs := make([]*unstructured.Unstructured, 0)
// Store the map of all sources having ref field into a map for applications with sources field
@@ -215,14 +215,20 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, sources []v1alp
return nil, nil, err
}
// GenerateManifest can return empty ManifestResponse without error if app has multiple sources
// and if any of the source does not have path and chart field not specified.
// In that scenario, we continue to the next source
if app.Spec.HasMultipleSources() && len(manifestInfo.Manifests) == 0 {
continue
}
targetObj, err := unmarshalManifests(manifestInfo.Manifests)
if err != nil {
return nil, nil, err
}
targetObjs = append(targetObjs, targetObj...)
manifestInfos = append(manifestInfos, manifestInfo)
manifestInfoMap[&source] = manifestInfo
}
ts.AddCheckpoint("unmarshal_ms")
@@ -232,7 +238,7 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, sources []v1alp
}
logCtx = logCtx.WithField("time_ms", time.Since(ts.StartTime).Milliseconds())
logCtx.Info("getRepoObjs stats")
return targetObjs, manifestInfos, nil
return targetObjs, manifestInfoMap, nil
}
func unmarshalManifests(manifests []string) ([]*unstructured.Unstructured, error) {
@@ -393,7 +399,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
var targetObjs []*unstructured.Unstructured
now := metav1.Now()
var manifestInfos []*apiclient.ManifestResponse
var manifestInfoMap map[*v1alpha1.ApplicationSource]*apiclient.ManifestResponse
if len(localManifests) == 0 {
// If the length of revisions is not same as the length of sources,
@@ -405,7 +411,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
}
}
targetObjs, manifestInfos, err = m.getRepoObjs(app, sources, appLabelKey, revisions, noCache, noRevisionCache, verifySignature, project)
targetObjs, manifestInfoMap, err = m.getRepoObjs(app, sources, appLabelKey, revisions, noCache, noRevisionCache, verifySignature, project)
if err != nil {
targetObjs = make([]*unstructured.Unstructured, 0)
conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now})
@@ -428,7 +434,9 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
}
}
// empty out manifestInfoMap
manifestInfos = make([]*apiclient.ManifestResponse, 0)
for as := range manifestInfoMap {
delete(manifestInfoMap, as)
}
}
ts.AddCheckpoint("git_ms")
@@ -508,12 +516,12 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
}
manifestRevisions := make([]string, 0)
for _, manifestInfo := range manifestInfos {
for _, manifestInfo := range manifestInfoMap {
manifestRevisions = append(manifestRevisions, manifestInfo.Revision)
}
// restore comparison using cached diff result if previous comparison was performed for the same revision
revisionChanged := len(manifestInfos) != len(sources) || !reflect.DeepEqual(app.Status.Sync.Revisions, manifestRevisions)
revisionChanged := len(manifestInfoMap) != len(sources) || !reflect.DeepEqual(app.Status.Sync.Revisions, manifestRevisions)
specChanged := !reflect.DeepEqual(app.Status.Sync.ComparedTo, appv1.ComparedTo{Source: app.Spec.GetSource(), Destination: app.Spec.Destination, Sources: sources})
_, refreshRequested := app.IsRefreshRequested()
@@ -674,13 +682,13 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
healthStatus, err := setApplicationHealth(managedResources, resourceSummaries, resourceOverrides, app, m.persistResourceHealth)
if err != nil {
conditions = append(conditions, appv1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: fmt.Sprintf("error setting app health: %s", err.Error()), LastTransitionTime: &now})
conditions = append(conditions, appv1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now})
}
// Git has already performed the signature verification via its GPG interface, and the result is available
// in the manifest info received from the repository server. We now need to form our opinion about the result
// and stop processing if we do not agree about the outcome.
for _, manifestInfo := range manifestInfos {
for _, manifestInfo := range manifestInfoMap {
if gpg.IsGPGEnabled() && verifySignature && manifestInfo != nil {
conditions = append(conditions, verifyGnuPGSignature(manifestInfo.Revision, project, manifestInfo)...)
}
@@ -697,11 +705,11 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
}
if hasMultipleSources {
for _, manifestInfo := range manifestInfos {
for _, manifestInfo := range manifestInfoMap {
compRes.appSourceTypes = append(compRes.appSourceTypes, appv1.ApplicationSourceType(manifestInfo.SourceType))
}
} else {
for _, manifestInfo := range manifestInfos {
for _, manifestInfo := range manifestInfoMap {
compRes.appSourceType = v1alpha1.ApplicationSourceType(manifestInfo.SourceType)
break
}

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