Compare commits

...

382 Commits

Author SHA1 Message Date
Alexander Matyushentsev
ebbc1d02f5 chore: pin mkdocs version to fix docs build (#6421)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2021-07-11 13:20:59 +02:00
Saumeya Katyal
9b0631b6c6 fix: Version warning banner in docs (#6682)
Signed-off-by: saumeya <saumeyakatyal@gmail.com>

add side-bar media queries

removed extra comments

Signed-off-by: saumeya <saumeyakatyal@gmail.com>
2021-07-11 13:12:28 +02:00
argo-bot
eb3d1fb84b Bump version to 1.8.7 2021-03-03 07:02:37 +00:00
argo-bot
e97b643526 Bump version to 1.8.7 2021-03-03 07:02:20 +00:00
kshamajain99
b51ba85aae fix: don't log certain fields (#5662)
* fix: support longer cookie

Signed-off-by: kshamajain99 <kshamajain99@gmail.com>

* merge conflicts

Signed-off-by: kshamajain99 <kshamajain99@gmail.com>

* fix: don't log certain fields

Signed-off-by: kshamajain99 <kshamajain99@gmail.com>
2021-03-02 21:32:29 -08:00
Regina Scott
be72e7cc42 fix: docs version selector not rendering (#5649)
Signed-off-by: Regina Scott <rescott@redhat.com>
2021-03-01 22:00:14 -08:00
Jan Gräfen
f6e9e41d7b fix: Empty resource whitelist allowed all resources (#5540) (#5551)
* fix: Empty resource whitelist allowed all resources

This requires setting the default in quite a few
places around the code base as well as adapting
a couple of tests

Signed-off-by: Jan Graefen <223234+jangraefen@users.noreply.github.com>

* Improve default behavior and not require explicitly set whitelist

Signed-off-by: Jan Graefen <223234+jangraefen@users.noreply.github.com>
2021-03-01 10:22:08 -08:00
jannfis
087b8b2fd5 docs: Move security policy to SECURITY.md for integration with GitHub (#5627)
* docs: Move security policy to SECURITY.md for integration with GitHub

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

* Change wording a bit.

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

* Change order of e-mail addresses

Signed-off-by: jannfis <jann@mistrust.net>
2021-02-27 15:17:53 +01:00
argo-bot
6dbbb18aa9 Bump version to 1.8.6 2021-02-26 21:12:06 +00:00
argo-bot
62b9b3aeb5 Bump version to 1.8.6 2021-02-26 21:11:50 +00:00
jannfis
31110cde4d fix: Properly escape HTML for error message from CLI SSO (#5563)
Signed-off-by: jannfis <jann@mistrust.net>
2021-02-26 10:29:50 +01:00
Alexander Matyushentsev
d6c5c72eb4 fix: API server should not print resource body when resource update fails (#5617)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2021-02-25 19:25:22 -08:00
kshamajain99
0b3333ef4b fix: fix memory leak in application controller (#5604)
fix: fix memory leak in application controller
2021-02-25 19:25:18 -08:00
argo-bot
d0f8edfec8 Bump version to 1.8.5 2021-02-20 05:29:23 +00:00
argo-bot
b1ff29fdf9 Bump version to 1.8.5 2021-02-20 05:29:05 +00:00
Alexander Matyushentsev
785bb9ecce fix: 'argocd app wait --suspended' stuck if operation is in progress (#5511)
* fix: 'argocd app wait --suspended' stuck if operation is in progress

Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2021-02-19 14:41:58 -08:00
Alexander Matyushentsev
b57579c4ae fix: Presync hooks stop working after namespace resource is added in a Helm chart #5522 2021-02-19 09:50:56 -08:00
Ajay Kemparaj
6b53ac785e docs: add the missing rbac resources to the documentation (#5476)
* Adds resources accounts and gpgkeys

Signed-off-by: ajayk <ajaykemparaj@gmail.com>
2021-02-13 09:05:07 +01:00
Alexander Matyushentsev
e38920f570 refactor: optimize argocd-application-controller redis usage (#5345)
* refactor: controller uses two level caching to reduce number of redis calls

Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2021-02-05 10:24:03 -08:00
argo-bot
28aea3dfde Bump version to 1.8.4 2021-02-05 17:46:03 +00:00
argo-bot
fe59190a96 Bump version to 1.8.4 2021-02-05 17:45:46 +00:00
Alexander Matyushentsev
0a04a491d9 fix: version info should be avaialble if anonymous access is enabled (#5422)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2021-02-05 09:17:43 -08:00
kshamajain99
701ce05393 fix: disable jwt claim audience validation #5381 (#5413)
* fix: disable audience validation

Signed-off-by: kshamajain99 <kshamajain99@gmail.com>

* update other places

Signed-off-by: kshamajain99 <kshamajain99@gmail.com>
2021-02-05 09:17:39 -08:00
Alexander Matyushentsev
965825f752 fix: /api/version should not return tools version for unauthenticated requests (#5415)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2021-02-05 09:17:31 -08:00
Alexander Matyushentsev
bd73326b8a fix: account tokens should be rejected if required capability is disabled (#5414)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2021-02-05 09:16:56 -08:00
Alexander Matyushentsev
502b8944c4 feat: set X-XSS-Protection while serving static content (#5412)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2021-02-05 09:16:52 -08:00
Alexander Matyushentsev
f5b0db240b fix: tokens keep working after account is deactivated (#5402)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2021-02-05 09:16:48 -08:00
Alexander Matyushentsev
ce43b7a438 fix: a request which was using a revoked project token, would still be allowed to perform requests allowed by default policy (#5378)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2021-02-05 09:16:44 -08:00
Liviu Costea
ebcfea64ff refactor(jwt): use typed access to claims (#5075)
Signed-off-by: Liviu Costea <email.lcostea@gmail.com>
2021-02-05 09:16:31 -08:00
Regina Scott
14c3dd2c59 fix: overriding version logic in warning banner (#5410)
Signed-off-by: Regina Scott <rescott@redhat.com>
2021-02-04 12:53:36 -08:00
Regina Scott
4359d345a0 feat: add versioning to argocd docs (#5099)
* feat: add versioning to argocd docs

Signed-off-by: Regina Scott <rescott@redhat.com>

* make default branch stable, provide warning for latest

Signed-off-by: Regina Scott <rescott@redhat.com>
2021-02-04 12:24:34 -08:00
Regina Scott
7081068a2d fix: Capitalization in toc (#5024)
Signed-off-by: Regina Scott <rescott@redhat.com>
2021-02-04 12:24:24 -08:00
argo-bot
0f9c684278 Bump version to 1.8.3 2021-01-21 22:09:58 +00:00
argo-bot
3ea3c13665 Bump version to 1.8.3 2021-01-21 22:09:44 +00:00
Alexander Matyushentsev
13fed83ec6 fix: make sure JWT token time fields contain only integer values (#5228)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2021-01-11 13:59:58 -08:00
argo-bot
94017f2c8d Bump version to 1.8.2 2021-01-10 05:30:48 +00:00
argo-bot
3c53ea6cff Bump version to 1.8.2 2021-01-10 05:30:32 +00:00
kshamajain99
7b2946962d updating cluster drops secret (#5220)
Signed-off-by: kshamajain99 <kshamajain99@gmail.com>
2021-01-09 12:21:12 -08:00
jannfis
df3422798d chore: Upgrade gorilla/handlers and gorilla/websocket (#5186)
* chore: Upgrade gorilla/handlers and gorilla/websocket

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

* go mod tidy

Signed-off-by: jannfis <jann@mistrust.net>
2021-01-06 12:23:33 +01:00
jannfis
f44855fa4d chore: Upgrade jwt-go to 4.0.0-preview1 (#5184)
Signed-off-by: jannfis <jann@mistrust.net>
2021-01-06 09:44:32 +01:00
Alexander Matyushentsev
1f4a052da3 fix: remove invalid assumption about OCI helm chart path (#5179)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2021-01-06 00:08:40 -08:00
jannfis
74f5eae750 fix: Possible nil pointer dereference in repository API (#5128)
Signed-off-by: jannfis <jann@mistrust.net>
2021-01-05 19:26:28 +01:00
jannfis
36a9465d85 fix: Possible nil pointer dereference in repocreds API (#5130)
Signed-off-by: jannfis <jann@mistrust.net>
2021-01-05 19:26:06 +01:00
Alexander Matyushentsev
9e6c04700e fix: use json serialization to store cache instead of github.com/vmihailenco/msgpack (#4965)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2021-01-04 22:42:04 -08:00
Alexander Matyushentsev
8abe96ad9a fix: add liveness probe to restart repo server if it fails to server tls requests (#5110) (#5119)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2021-01-04 22:36:29 -08:00
jannfis
10de6e7cfc fix: Allow correct SSO redirect URL for CLI static client (#5098)
Signed-off-by: jannfis <jann@mistrust.net>
2020-12-22 08:31:42 +01:00
May Zhang
e57071a150 fix: add grpc health check (#5060)
* fix: add grpc health check

Signed-off-by: May Zhang <may_zhang@intuit.com>

* fix: fixing lint error

Signed-off-by: May Zhang <may_zhang@intuit.com>

* fix: fixing lint error

Signed-off-by: May Zhang <may_zhang@intuit.com>
2020-12-15 13:21:24 -08:00
jannfis
41db5fc010 chore: Update Dex to v2.27.0 (#5058)
Signed-off-by: jannfis <jann@mistrust.net>
2020-12-15 18:23:16 +01:00
Alexander Matyushentsev
31f257e957 fix: setting 'revision history limit' errors in UI (#5035)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-12-11 10:35:09 -08:00
Alexander Matyushentsev
5fd93a7db5 fix: add api-server liveness probe that catches bad data in informer (#5026)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-12-11 10:35:02 -08:00
argo-bot
c2547dca95 Bump version to 1.8.1 2020-12-10 02:48:59 +00:00
argo-bot
522ed90f38 Bump version to 1.8.1 2020-12-10 02:48:47 +00:00
Alexander Matyushentsev
7a0266f0fb fix: sync retry is broken for multi-phase syncs (#5017)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-12-09 18:11:44 -08:00
argo-bot
30348417a9 Bump version to 1.8.0 2020-12-09 18:19:22 +00:00
argo-bot
910eddbbf3 Bump version to 1.8.0 2020-12-09 18:19:09 +00:00
Alexander Matyushentsev
f150ba18fb fix: infer app destination server in indexer to prevent concurrent app object modification (#4993)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-12-08 08:57:50 -08:00
jannfis
97d030b4b2 chore: Fix erroneous path expansion in release action (#4907)
Signed-off-by: jannfis <jann@mistrust.net>
2020-12-03 13:21:12 -08:00
Michael Goodness
5df269b3fa fix(repository.go): rename .argocd-source.yaml in error message (#4964)
Signed-off-by: Michael Goodness <michael.goodness@mlb.com>
2020-12-03 09:49:10 -08:00
argo-bot
775f9711e7 Bump version to 1.8.0-rc2 2020-12-03 04:59:41 +00:00
argo-bot
3f974825a6 Bump version to 1.8.0-rc2 2020-12-03 04:59:28 +00:00
Alexander Matyushentsev
1d55439f7f increase cache version (#4957)
Signed-off-by: Alexander Matyushentsev <Alexander_Matyushentsev@intuit.com>
2020-12-02 17:06:08 -08:00
Maxime Brunet
41daf71851 chore: Upgrade go-jsonnet to v0.17.0 (#4891)
* chore: Upgrade go-jsonnet to v0.17.0

Signed-off-by: Maxime Brunet <max@brnt.mx>

* Fix vm.EvaluateSnippet is deprecated

Use EvaluateFile or EvaluateAnonymousSnippet instead.

Signed-off-by: Maxime Brunet <max@brnt.mx>

* Do not read Jsonnet files

Signed-off-by: Maxime Brunet <max@brnt.mx>
2020-12-02 17:06:03 -08:00
Jesse Suen
f4796398a8 fix: rollout health could incorrectly report v0.9 rollouts as Progressing (#4949)
Signed-off-by: Jesse Suen <Jesse_Suen@intuit.com>
2020-12-02 13:45:04 -08:00
Alexander Matyushentsev
0f0b6ce278 fix: reset cached manifest generation errors after 1hr instead of 12 requests (#4953)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-12-02 13:37:45 -08:00
Alexander Matyushentsev
aee4dfaa1e fix: cache missing app path and commit verification errors (#4947)
* fix: cache missing app path and commit verification errors

Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-12-02 12:21:29 -08:00
Alexander Matyushentsev
74a92c6031 fix: upgrades github.com/vmihailenco/msgpack/v5 to fix #4933 (#4952)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-12-02 12:21:25 -08:00
Alexander Matyushentsev
4237c6f00f fix: correctly compare application destinations with inferred cluster URL (#4937)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-12-01 11:40:33 -08:00
Alexander Matyushentsev
0f3d74fa58 refactor: upgrade helm to v3.4.1 (#4938)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-12-01 11:40:27 -08:00
argo-bot
868516f0bd Bump version to 1.8.0-rc1 2020-11-25 18:03:58 +00:00
argo-bot
e50c43fb3f Bump version to 1.8.0-rc1 2020-11-25 18:03:44 +00:00
Alexander Matyushentsev
9b6a0dc3cd refactor: disable gRPC metrics by default (#4892)
Signed-off-by: Alexander Matyushentsev <Alexander_Matyushentsev@intuit.com>
2020-11-23 16:45:34 -08:00
Alexander Matyushentsev
1f63e99b78 docs: add v1.8 changelog and upgrading instructions (#4888)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-23 16:45:31 -08:00
Alexander Matyushentsev
589ad5d2ac fix: upgrade gitops-engine version. (fixes #4877) (#4890)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-23 13:02:59 -08:00
kshamajain99
4464f99df7 fix: validate empty server address for destination cluster (#4852) (#4860)
* Always set inferred destination server

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

Signed-off-by: jaideepr97 <jaideep.r97@gmail.com>
2020-11-20 11:21:11 -08:00
Sho Okada
bcaabc51a1 fix: argocd app patch remove does not work (#4585)
Signed-off-by: Sho Okada <shokada3@gmail.com>
2020-11-20 11:21:07 -08:00
Alexander Matyushentsev
9140fea0fb fix: increase max grpc message size (#4869)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-20 11:20:59 -08:00
Alexander Matyushentsev
d83e0ddb56 chore: use release tag to reference gitops engine dependency (#4866)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-19 14:10:31 -08:00
Mikhail Nacharov
25ca589bff fix: Adds podAntiAffinity in base manifests (#4549) (#4599)
Signed-off-by: Mikhail Vladimirovich Nacharov <author@webnach.ru>
2020-11-19 12:08:56 -08:00
Alex Gervais
6e6f4f50a0 docs: Improved documentation for Ambassador host-based routing configuration (#4857)
Signed-off-by: alex <alex@datawire.io>
2020-11-18 14:06:03 -08:00
Alexander Matyushentsev
ed547aa545 docs: add missing v1.7 patches description to changelog (#4859)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-18 10:09:01 -08:00
mitchharpur
ca7fa55a2b docs: Correct default docker desktop context name (#4830)
Signed-off-by: mitchharpur <mitchharpur@me.com>

Improved out of the box docker desktop instructions.
2020-11-18 08:52:33 -08:00
Alexander Matyushentsev
7ee951b5b8 fix: argocd diff --local should not print data of local secrets (#4850)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-17 09:30:35 -08:00
Alexander Matyushentsev
be8308199c docs: add automated sync policy allowEmpty to sample application (#4855)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-17 08:59:25 -08:00
Keith Chong
d8c08bfe7d test: Allow individual application tiles to be selectable (#4841) (#4851)
Signed-off-by: Keith Chong <kykchong@redhat.com>
2020-11-17 08:03:14 -08:00
Alexander Matyushentsev
231509bb3c feat: add project badge panel to project details page (#4846)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-17 08:02:07 -08:00
Alexander Matyushentsev
a2d6582e54 fix: fix rendering external links on application details page (#4847)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-16 23:48:31 -08:00
Alexander Matyushentsev
4b23918802 chore: add git operation metrics to sample dashboard (#4849)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-16 20:22:23 -08:00
Alexander Matyushentsev
51f2949883 feat: add jsonnet version to version panel (#4845)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-16 20:20:03 -08:00
Alexander Matyushentsev
ffa824bbba fix: use correct operation type to track ls-remote performance (#4848)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-16 20:19:46 -08:00
Keith Chong
e4e503aad7 feat: Add option to show branches or tags for GIT revision (#4751) (#4788)
Signed-off-by: Keith Chong <kykchong@redhat.com>
2020-11-16 13:04:17 -08:00
Alexander Matyushentsev
e8e810934d docs: document .argocd-source.yaml file usage (#4843)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-16 12:34:44 -08:00
jannfis
762b33c819 fix: Don't allow arbitrary redirects at login (#4780)
* fix: Don't allow arbitrary redirects at login

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

* Also check for CRLF in path

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

* Explicit unit test for empty redirect_url

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

* Cosmetics and remove redundancy

Signed-off-by: jannfis <jann@mistrust.net>
2020-11-16 20:30:36 +01:00
kshamajain99
69d8831b38 docs: improve description for API endpoint (#4821)
Signed-off-by: kshamajain99 <kshamajain99@gmail.com>
2020-11-16 09:03:15 -08:00
Jesse Suen
ad8715cbad feat: support argo-rollouts v0.10 (#4823)
Signed-off-by: Jesse Suen <Jesse_Suen@intuit.com>
2020-11-15 14:59:34 -08:00
jannfis
6cadaa2a5d chore: Update golang to v1.14.12 (#4832)
Signed-off-by: jannfis <jann@mistrust.net>
2020-11-15 09:33:46 -08:00
Oleg Sucharevich
8b1a118cdb docs: add codefresh (#4833)
Signed-off-by: Oleg Sucharevich <oleg2807@gmail.com>
2020-11-15 09:10:01 -08:00
Sven Walter
ccb7371047 fix: improve commit verification tolerance (#4825)
The `git verify-commit` output might have additional fields like
`issuer`. This change will make the parser skip the additional fields
instead of returning an error.

    gpg: Signature made Mon Aug 26 20:59:48 2019 CEST
    gpg:                using RSA key 4AEE18F83AFDEB23
    gpg:                issuer "j.doe@example.com"
    gpg: Can't check signature: No public key

This change is designed so it is easy to specify additional fields that
need to get skipped, by adjusting the regex.

Signed-off-by: Sven Walter <s.walter@rebuy.com>
2020-11-13 16:43:44 +01:00
Jesse Suen
2f72f3adad fix: sync hooks using generateName was using fixed timestamp suffix (#4787)
Signed-off-by: Jesse Suen <Jesse_Suen@intuit.com>
2020-11-13 01:18:15 -08:00
sethp-verica
ae17c70b00 docs: add Google SAML integration example (#4816)
Signed-off-by: Seth Pellegrino <seth@verica.io>
2020-11-12 14:18:48 -08:00
jannfis
5921feda5f chore: Cleanup Helm testdata after test (#4815)
Signed-off-by: jannfis <jann@mistrust.net>
2020-11-11 10:49:01 -08:00
Alexander Matyushentsev
71bd3fdd24 fix: add FIRST OCCURRED, LAST OCCURRED to events table (#4812)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-11 17:57:08 +01:00
Relk Li
30816bc549 docs: add WeMo Scooter to USERS.md (#4813)
Signed-off-by: Relk Li <YiJiun.Li.C@gmail.com>
2020-11-11 08:38:50 -08:00
Simon Rüegg
0681c2754a feat: Add health checks for Crossplane providers (#4778)
Signed-off-by: Simon Rüegg <simon@rueggs.ch>
2020-11-10 13:57:21 -08:00
William Tam
5040d6f080 fix: add roles to redis-ha service accounts to enable run-as non-root users in OpenShift (#4800)
Signed-off-by: William Tam <email.wtam@gmail.com>
2020-11-10 13:53:33 -08:00
William Tam
e3b733627f docs: Generate server command reference (#4795)
* Generate  server command reference docs
Signed-off-by: William Tam <email.wtam@gmail.com>

* Insert newline
Signed-off-by: William Tam <email.wtam@gmail.com>

* undo iinsert newline
Signed-off-by: William Tam <email.wtam@gmail.com>

Signed-off-by: William Tam <email.wtam@gmail.com>

* * Renane tab to `Server Configuration Parameters`
* Generate argocd-util command docs

Signed-off-by: William Tam <email.wtam@gmail.com>

* Tweak erver command  descriptions

Signed-off-by: William Tam <email.wtam@gmail.com>

* Minor tweaks to argocd-util command descriptions

Signed-off-by: William Tam <email.wtam@gmail.com>

* Move main_test.go to secrets_redactor_test.go into commands package
Signed-off-by: William Tam <email.wtam@gmail.com>

* Disable auto generation tag.

Signed-off-by: William Tam <email.wtam@gmail.com>
2020-11-10 20:01:11 +01:00
Jonathan West
be513e431a chore: Fix bulleted list in Global Projects section of projects (#4803)
Signed-off-by: Jonathan West <jonwest@redhat.com>
2020-11-10 19:34:26 +01:00
Xavier Krantz
5e24d21ae8 docs: Add Virtuo to USERS.md (#4805)
Virtuo (govirtuo.com) is officially using ArgoCD along with the other Argo projects to deploy to Kubernetes.

Signed-off-by: Xavier Krantz <xakraz@gmail.com>
2020-11-10 19:33:52 +01:00
Josh Soref
eec8f79923 fix: Clarify in multiple applications (#4769) (#4771)
Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-11-09 10:21:24 -08:00
Ashley Manraj
d1ba640bff docs: add 3rein to users (#4797)
Signed-off-by: Ashley Manraj <ashley@3rein.com>
2020-11-09 10:11:40 -08:00
jannfis
7947b59eeb chore: Fix helm2 dependency tests (#4789)
Signed-off-by: jannfis <jann@mistrust.net>
2020-11-07 10:07:51 -08:00
Jonathan West
4c3f97f78a chore: Add a GitHub action that runs unit tests with -race to CI build (#4774) (#4775)
* chore: Add a GitHub action that runs unit tests with -race to CI build (#4774)

Signed-off-by: Jonathan West <jonwest@redhat.com>

* chore: Add a GitHub action that runs unit tests with -race to CI build (#4774)

Signed-off-by: Jonathan West <jonwest@redhat.com>
2020-11-07 12:57:18 +01:00
Alexander Matyushentsev
c4dcae3442 fix: improve project details page summary tab (#4782)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-06 16:00:45 -08:00
Jesse Suen
d1a36e5b6d feat: add new rollout actions (abort, retry, promote-full, terminate). improve health check (#4777)
Signed-off-by: Jesse Suen <jesse_suen@intuit.com>
2020-11-06 15:16:32 -08:00
kshamajain99
9c51838ccc Chore: Update broken link in conrtibution guide (#4785) 2020-11-06 14:44:45 -08:00
Hu Sheng
700a4104c6 docs: add openEuler, openGauss, openLooKeng, MindSpore to users (#4779) 2020-11-06 14:09:25 -08:00
William Tam
dcb5f07c23 docs: Add documentation to run redis-ha in OpenShift (#4759)
Signed-off-by: William Tam <email.wtam@gmail.com>
2020-11-06 14:00:10 -08:00
Josh Soref
9625e50ccd fix: Use ghost icon for unknown/missing permissions (#4730) (#4783)
Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-11-06 12:29:10 -08:00
jannfis
3e19b2fdf1 chore: Update redis to 5.0.10 (#4767)
Signed-off-by: jannfis <jann@mistrust.net>
2020-11-05 13:58:26 -08:00
Amit Rout
96e0f0d3be docs: Add github reference to k3d in developer-guide (#4761)
Signed-off-by: arout <arout@redhat.com>
2020-11-05 13:57:59 -08:00
May Zhang
620e31e52d fix: From UI create or delete JWTToken, error "'metadata' of undefined" (#4766)
Signed-off-by: May Zhang <may_zhang@intuit.com>
2020-11-05 13:57:19 -08:00
Paweł Hajduk
ab7e1773f0 docs: Updated OneLogin documentation of clientSecret property (#4763) 2020-11-05 21:31:41 +01:00
Tim Etchells
e67d934827 docs: Add myself as reviewer (#4762)
Signed-off-by: Tim Etchells <tetchell@redhat.com>
2020-11-05 16:35:58 +01:00
Jonathan West
9ee0d2c6c0 docs: proposal to add jgwest as a reviewer (#4757)
Signed-off-by: Jonathan West <jonwest@redhat.com>
2020-11-04 20:40:44 -08:00
William Tam
9c684ddc08 Proposal to add wtam2018 as a reviewer (#4756)
* Add wtam2018 as a reviewer

* remove "P"  that  gets prepended
2020-11-04 18:06:21 -08:00
jannfis
c9f3c64a58 chore: Fail-fast in Makefile when not within $GOPATH (#4744)
Signed-off-by: jannfis <jann@mistrust.net>
2020-11-04 14:01:01 -08:00
jannfis
8c3a8e3655 docs: Remove deprecated warnig for local users (#4750)
Signed-off-by: jannfis <jann@mistrust.net>
2020-11-04 20:36:02 +01:00
May Zhang
449b50cf6c fix: failUnmarshal only checks for file resembles a resource (#4729)
* fix: failUnmarshal only checked for file contains information which has apiVersion, kind and metadata.

Signed-off-by: May Zhang <may_zhang@intuit.com>

* add negative test case

Signed-off-by: May Zhang <may_zhang@intuit.com>

* modified negative test data

Signed-off-by: May Zhang <may_zhang@intuit.com>

* modified negative test data

Signed-off-by: May Zhang <may_zhang@intuit.com>
2020-11-03 15:46:51 -08:00
Alexander Matyushentsev
da3ab59be0 refactor: upgrade helm to v3.4.0 (#4745)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-03 11:48:00 -08:00
Alexander Matyushentsev
e8f63d4583 feat: remove unnecessary fields from project creation panel (#4742)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-03 11:43:14 -08:00
Alexander Matyushentsev
cdb3df1077 fix: Argo CD should present the live object in the resource version in git (#4740)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-03 11:24:14 -08:00
Isaac Gaskin
858676c4f8 chore: helm2 verison bump (#4724)
* chore: helm2 verison bump
2020-11-03 11:22:14 -08:00
Josh Soref
44c31e278c Allow deny (#4293) (#4294)
* chore(ui): switch to using allow/deny (#4293)

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-11-03 10:54:13 -08:00
jannfis
a759601264 fix: Only verify GPG signatures on metadata requests when verification is enforced (#4741)
Signed-off-by: jannfis <jann@mistrust.net>
2020-11-03 18:43:02 +01:00
Alexander Matyushentsev
9bbbda55a4 fix: use fmt.Sprintf to format complex logging fields (#4739)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-03 08:41:08 -08:00
Alexander Matyushentsev
b97f4f7f8e fix: RevisionFormField component crashes in 'refs' API returns no tags (#4735)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-11-02 22:56:00 -08:00
phillip-ops
422a26e8d8 docs: add Opensurvey to USERS.md (#4727) 2020-11-02 14:16:44 -08:00
nahidupa
245e1ee636 docs: correct parameters usage in CLI (#4725)
Adding guestbook= before the value does not require. Removed that.
2020-11-02 14:16:09 -08:00
Jonathan West
2166fea351 fix: Repo-server has silent unmarshalling errors leading to empty applications (#4423) (#4708)
* fix: Repo-server has silent unmarshalling errors leading to empty applications (#4423)
2020-11-02 14:15:31 -08:00
Jesse Suen
dea75eb481 fix: inject artificial delay between sync waves to better support health assessments (#4715)
Signed-off-by: Jesse Suen <jesse_suen@intuit.com>
2020-11-02 12:17:43 -08:00
May Zhang
6ef89e3c09 fix: exclude files listed under exclusions (#4686)
* fix: exclude files listed under exclustions

* fix: add it to cli

* fix: update doc

* fix: added e2e test

* fix: fix merge conflicts

* fix: fix merge conflicts

* fix: fix merge conflicts

* use glob

* fix merge conflict

* fix rename the flag

* This is my commit message

Signed-off-by: May Zhang <may_zhang@intuit.com>

* -s
2020-11-02 08:46:02 -08:00
Jesse Suen
b9954e55ac feat: support resource actions on CRDs that use status subresources (#4690)
Signed-off-by: Jesse Suen <Jesse_Suen@intuit.com>
2020-11-02 02:09:05 -08:00
Tim Etchells
86031504af feat: Add autocomplete for repo Revisions (#4645) (#4713)
* feat: Add autocomplete for repo Revisions

- Introduces api/v1/repositories/{repo}/refs which returns branches and tags
- Add new RevisionFormField component to Create and Edit Application pages

Signed-off-by: Tim Etchells <tetchell@redhat.com>
2020-11-01 19:25:37 -08:00
Alexander Matyushentsev
97003caebc fix: webhook don't refresh apps pointing to HEAD (#4717) 2020-10-30 11:13:03 -07:00
J. Mark Pim
42ebb227e1 feat: Add support for ExecProvider cluster auth (#4600) (#4710) 2020-10-30 09:58:45 -07:00
John Pitman
c7f7631f2e fix: adding helm values file in New App (#4635)
Signed-off-by: John Pitman <jpitman@redhat.com>
2020-10-30 09:47:14 -07:00
Dewan Ahmed
9cdfe40faf docs: Instructions on make verify-kube-connect step when using k3d (#4687) 2020-10-30 09:07:13 -07:00
Alexander Matyushentsev
21304ee2c5 feat: Annotation based app paths detection in webhooks (#4699)
* feat: Annnotation based app prefix detection in webhooks

* webhook should copy previously generated manifests cache if new commit does not introduce any changes

* use 'argocd.argoproj.io/manifest-generate-paths' annotation to specify paths

Co-authored-by: Carson Anderson <ca@carsonoid.net>
2020-10-29 13:17:54 -07:00
Isaac Gaskin
7f0ffb4cd2 fix: adding commonAnnotations for Kustomize (#4613)
* fix: adding commonAnnotations

optional k/v map to add annotations via kustomize

* fix: adding kustomize common annotations

also correcting kustomize cli flags to respect multiple options

#4613
2020-10-29 19:12:55 +01:00
Darshan Chaudhary
22e0b4ff55 fix: add flag to indicate end of logs (#4696)
Signed-off-by: darshanime <deathbullet@gmail.com>
2020-10-29 09:37:59 -07:00
Tim Etchells
0767dff025 feat(ui): Improve UX when filtering applications (#4403) (#4622)
* feat: Improve UX when filtering applications

- When application filters are active, show a badge with # filters and a button to clear them
- When no matching applications are found, provide a button to clear filters
- Styling updates to application filters UI

Signed-off-by: Tim Etchells <tetchell@redhat.com>
2020-10-29 09:08:45 -07:00
Keith Chong
5ba8710ff1 docs: Describe Argo UI dependency changes in dependencies.md (#4698) (#4702)
Signed-off-by: Keith Chong <kykchong@redhat.com>
2020-10-29 08:56:00 -07:00
jannfis
dde5f143fc chore: Fix failing unit test for TestGetIstioVirtualServiceInfo (#4711) 2020-10-29 08:38:06 -07:00
William Tam
f02115af15 fix: add argocd-redis SA to nonroot security context constraint (#4660) 2020-10-28 16:30:09 -07:00
Omer Kahani
cd302fd055 feat: add Istio virtual service to the network view - part of #4675 (#4677) 2020-10-28 16:24:06 -07:00
Remington Breeze
cfd59aded2 fix(ui): stack overflow crash of resource tree view for large applications (#4685) 2020-10-28 15:57:16 -07:00
May Zhang
dd856e1c2b feat: Util for generate global project from ClusterRole (#4653)
* feat: Util for generate global project from ClusterRole

* feat: fix lint error

* feat: fix test failure

* fix lint

* fix lint error

* moved into sub command and removed not needed fields.

* updated error package

* rename files
2020-10-27 16:54:47 -07:00
Shuwei Hao
387f775f4a feat: Support oci-based helm repository (#4018)
* feat: Support oci-based helm repository

Signed-off-by: haoshuwei <haoshuwei24@gmail.com>
2020-10-27 15:37:42 -07:00
Jason
837ed45361 docs: Clarify where hook manifests go (#4659) 2020-10-27 14:59:32 -07:00
Keith Chong
46ee2f21a2 fix: Automated smoke test that mimics user behavior UI (#4393) (#4683)
Signed-off-by: Keith Chong <kykchong@redhat.com>
2020-10-27 14:55:10 -07:00
Josh Soref
3bf9deb15e chore: Remove newline from new issue template (#4681)
Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-10-27 14:14:44 -07:00
Alexander Matyushentsev
a96b476f16 refactor: upgrade gitops-engine version ( breaking API changes related to logr mirgation ) (#4652) 2020-10-27 14:10:24 -07:00
Tim Etchells
764ea07fc0 feat: Add 'proj role list-tokens' command (#4674)
Signed-off-by: Tim Etchells <tetchell@redhat.com>
2020-10-27 12:56:37 -07:00
Marco Londero
6ddd98c4f8 docs: add tru.ID to USERS (#4672) 2020-10-27 09:20:29 -07:00
Tim Etchells
be60425a47 feat(cli): Add ID option to 'proj role create-token' (#4632) (#4636)
* feat: Add ID option to 'proj role create-token'

Also add some more informative output to the same command

Signed-off-by: Tim Etchells <tetchell@redhat.com>

* Parse token on client side, add --token-only flag

Signed-off-by: Tim Etchells <tetchell@redhat.com>
2020-10-27 08:25:07 +01:00
Alexander Matyushentsev
0850bcc184 fix: application list page crash if app referencing not configured cluster URL (#4671) 2020-10-26 17:43:46 -07:00
Alexander Matyushentsev
aaae4003a0 fix: fix nil pointer error in Argo CD repo server (#4668) 2020-10-26 15:29:30 -07:00
jannfis
6535d1ac34 chore: Change log level in updateClusters() from info to debug (#4667) 2020-10-26 14:07:34 -07:00
Adrian Ludwin
81b84e66c1 docs: Remove obsolete instructions for GKE (#4658) 2020-10-26 14:03:47 -07:00
Alexander Matyushentsev
5fdbe2057a feat: support generating manifests for the same commit in parallel (#4625) 2020-10-26 13:32:18 -07:00
Tim Etchells
ac8d18d39d chore: Update developer guide docs (#4664)
- Remove references to CircleCI
- Move info for troubleshooting CI builds into ci.md
- Add 'gofmt' note to Lint CI check failing
- Add note about generated code to FAQ

Signed-off-by: Tim Etchells <tetchell@redhat.com>
2020-10-26 19:48:43 +01:00
May Zhang
3d39accdb2 feat: Global Project UI (#4587)
* feat: Global Project UI

* feat: fixing compile error

* feat: rename import

* feat: Fixing TypeError: Cannot read property 'spec' of undefined when editing existing fields of project.

* feat: Fixing lint error.

* feat: Fixed issue when editing project fields, virtual project had stale info

* feat: fixing lint

* feat: add GetGlobalProjects

* feat: fixing swagger conflict.

* feat: fixing undefined.

* feat: update swagger.json

* feat: update doc

* feat: update doc

* feat: update doc with version number

* Load project and global projects in parallel

* feat: hide global project info.

* feat: lint

* feat: fixed edit issue

* feat: dedup

* feat: fix lint error

Co-authored-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-10-26 10:39:39 -07:00
Chetan Banavikalmutt
4d643a151d feat: Include cluster name in application filter (#4529) 2020-10-23 18:54:10 -07:00
Marcel Hoyer
41ab92fbdd docs: Fix broken 'Dex Connectors' link (#4647)
The documentation of Dex was materialized to https://dexidp.io/docs/ in the meantime.
2020-10-23 12:08:01 -07:00
Jonathan West
2e06118792 fix: Data race between processAppRefreshQueueItem and processAppOperationQueueItem, in appcontroller.go (#4643) (#4644) 2020-10-23 12:05:41 -07:00
Tim Etchells
bbfbf6834f fix: Add tooltips to Role JWTs view (#4642)
- Improve error message for invalid 'Expires In'
- Remove extra space next to 'Create' button

Signed-off-by: Tim Etchells <tetchell@redhat.com>
2020-10-23 19:43:34 +02:00
Mike Bryant
c7dbe4883b feat: Add additional printer columns for Health and Sync status (#4641) 2020-10-22 14:07:05 -07:00
Jaideep Rao
bdee71d4c7 fix: Pick up correct commit SHA when using annotated git tags (#4231) (#4538) 2020-10-22 13:06:36 -07:00
lonfme
9af729c738 fix: ssh repo url match failed when webhook github http custom port (#4532) 2020-10-22 13:05:05 -07:00
Josh Soref
c0f9c9ae93 chore(ui): Login page link should be "Log in" (the verb phrase) (#4611) (#4612)
Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-10-22 09:57:48 -07:00
yutachaos
dfc75df7a0 docs: Update USERS.md (#4638)
add Quipper
2020-10-22 09:38:23 -07:00
Jean-Luc Geering
cf03c1dcc5 docs: Update USERS.md (#4630)
add UFirstGroup
2020-10-21 20:47:06 +02:00
jannfis
7824a1fc2b chore: Upload e2e server component logfiles to GH action on failures (#4565) 2020-10-21 07:59:24 -07:00
Josh Soref
5db8d97bf0 chore: Update swagger docs (#4610) (#4616)
Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-10-20 20:28:57 +02:00
Josh Soref
05c493b3a6 Spelling (#4596) 2020-10-19 18:21:06 -07:00
William Tam
894f95dce5 docs: Add generated argocd command docs (#4606) 2020-10-19 17:30:32 -07:00
Vincent Gilles
90227f226d docs: describe application creation from helm repository in declarative setup documentation (#4463) (#4597) 2020-10-19 11:21:50 -07:00
jannfis
fd482316d0 fix: Serve ReDoc JS from Argo CD server instead of from CDN (#4594)
* fix: Serve ReDoc JS from Argo CD server instead of from CDN

* refactor: dynamically copy redoc.standalone.js from npm module

Co-authored-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-10-19 20:01:45 +02:00
Tim Etchells
c620fa7aaa fix: only bundle the yaml language for Monaco (#4582)
Signed-off-by: Tim Etchells <tetchell@redhat.com>
2020-10-19 10:59:21 -07:00
jannfis
a0f6e033c0 chore: Replace deprecated commands for release action (#4593) 2020-10-19 10:40:39 -07:00
jannfis
15b0a6e793 chore: Replace deprecated GH actions directives for integration tests (#4589)
* chore: Replace deprecated set-env directives

* revert lint version change

* Revert go.mod and go.sum changes

* Fix typo

* Update golangci-lint-action to v2

* Fix golangci-lint version

* Skip new lint complaints in test

* Skip more new lint complaints in test

* Exclude new SA5011 check in lint
2020-10-17 09:03:57 +02:00
Jason DeWitt
3408e2d72d docs: Add clarification on the initial password to Getting Started guide 2020-10-16 14:10:48 -07:00
Victor Boissiere
e09cacba65 docs: Add new company to user list (#4588) 2020-10-16 15:35:56 +02:00
Ricardo Contreras
66d86fe56f docs: update upgrading overview doc to include missed versions (#4592)
add missing links to 1.5->1.6 and 1.6->1.7 upgrades notes.
2020-10-16 15:35:02 +02:00
Tim Etchells
67c91564c4 fix: don't re-pull fonts on each webpack build (#4584)
Signed-off-by: Tim Etchells <tetchell@redhat.com>
2020-10-15 19:32:00 -07:00
jannfis
698712f396 fix: Make gpg watcher re-creation more robust (#4580) 2020-10-15 15:20:37 -07:00
Alex Gervais
001d990d0c docs: Add documentation for Ambassador ingress options (#4575) 2020-10-15 13:56:55 -07:00
Remington Breeze
1cbada9d86 fix(ui): UI crash when ksonnet app has no environments (#4566) 2020-10-15 11:20:54 -07:00
Chetan Banavikalmutt
dff7da7271 fix: Infer destination server when latest version of app is fetched (#4574) 2020-10-15 08:57:18 -07:00
Alexander Matyushentsev
12957a494c chore: fix flaky TestFinalizeAppDeletion/ErrorOnBothDestNameAndServer test (#4571) 2020-10-15 13:22:06 +02:00
Jonathan West
4f1e371830 fix: Data race in SettingsManager.GetRepositoryCredentials() (#4561) (#4562) 2020-10-15 13:18:38 +02:00
Alexander Matyushentsev
8e11facb94 refactor: upgrade gitops-engine version ( #4354, #1787 ) (#4563) 2020-10-14 14:05:09 -07:00
Kevin McDermott
61c8f73e21 Don't parse empty strings as maps in settings. (#4556)
There's a bug in the resource inclusions parsing, if the string is "" then it's
parsed as a map, which returns nil, and so it fails when adding elements later.
2020-10-14 12:15:58 -07:00
May Zhang
23ac24bdea fix: login with apiKey capability (#4557)
* fix: login with apiKey capability

* fix: update based on code review.

* fix: update based on code review.

* fix: check pws first.
2020-10-14 11:31:35 -07:00
Alexander Matyushentsev
e6f116319b fix: editing Clusers in the UI drops credentials from the secret (#4553) 2020-10-14 10:45:58 -07:00
Chetan Banavikalmutt
5fa808a788 fix: Infer cluster URL while deleting app resource (#4559)
An application can be created by specifying only cluster name. Since cluster URL is used for queries, it should inferred. ValidateDestination() method will infer the cluster URL if cluster name is present.

Fixes: #4534
2020-10-14 10:32:41 -07:00
Jonathan West
6d64280fba fix: Data race in SettingsManager.GetRepositories(), via util/db unit tests (#4550) (#4551) 2020-10-14 15:16:22 +02:00
William Tam
3ae1d13dfd refactor: update gitops engine version (issue #1816) (#4552) 2020-10-13 21:22:36 -07:00
May Zhang
f512d213cf feat: global project (#4506)
* feat: global project

* feat: revert back argocd-cm.yaml

* feat: remove commented code.

* feat: check err

* feat: corrected comments.

* feat: merge sync windows

* feat: getProject

* feat: fix lint error

* feat: update existing test case

* feat: minor comments

* feat: Fixed for sync window which is also called from API server.

* feat: fix application tests

* feat: block by sync window

* feat: test using sync window

* feat: updated based on code review

* feat: fixed comment
2020-10-13 15:12:05 -07:00
Viktor Farcic
35914ff7ab docs: YouTube video on how to create PR (preview) environments with Argo CD (#4544) 2020-10-13 14:13:22 -07:00
William Tam
48891e2536 chore: rename cli-docker -> cli-argocd (#4527) 2020-10-13 14:05:23 -07:00
Jonathan West
a88c729148 fix: Data races detected while running 'server/application' unit tests (#4546) (#4547) 2020-10-13 13:49:45 -07:00
jannfis
c8ca3e7c45 chore: Run e2e tests against different K8s versions (#4444) 2020-10-13 13:37:05 -07:00
Alexander Matyushentsev
2a0012d5f1 refactor: upgrade k8s client to v0.19.2 (#4545) 2020-10-13 13:01:26 -07:00
William Tam
fd483babb7 docs: replace deployment with statefulset (#4531) 2020-10-13 11:30:21 -07:00
Tim Etchells
52f4ed203f fix: Revert "fix: webpack-dev-server proxy hostname (#4515)" (#4530)
This reverts commit 8eb3306064.
2020-10-11 08:16:20 -07:00
Alexander Matyushentsev
497cd603ca docs: clarify project RBAC management documentation (#4526) 2020-10-09 16:51:14 -07:00
Adam Blackwell
97f094756d Add edX to USERS (#4524) 2020-10-09 16:17:03 -07:00
Alexander Matyushentsev
5cdcca4544 fix: bump cache version to avoid nil pointer error (#4525) 2020-10-09 16:16:38 -07:00
Jonathan West
d479d22de7 feat: Failed manifest generation protection (#4238) (#4430) 2020-10-09 13:47:31 -07:00
Alexander Matyushentsev
303925f4a0 feat: Support controller horizontal scaling (#4285) 2020-10-09 13:16:54 -07:00
Tim Etchells
8eb3306064 fix: webpack-dev-server proxy hostname (#4515)
Switching the hostname from 'localhost' to the ipv6 '[::1]' fixes the dev server proxy

https://github.com/webpack/webpack-dev-server/issues/793#issuecomment-316650146

Signed-off-by: Tim Etchells <tetchell@redhat.com>
2020-10-09 12:55:28 -07:00
Keith Chong
9f2eab665b fix: Summary legend overflow view (#4523)
Signed-off-by: Keith Chong <kykchong@redhat.com>
2020-10-09 12:08:33 -07:00
Alexander Matyushentsev
3ac0bc36d4 fix: api-server should not try creating default project it is exists already (#4517) 2020-10-09 10:17:00 -07:00
Timothy OBrien
2b84672641 docs: minor spelling fix to azure sso docs (#4518) 2020-10-09 09:45:48 -07:00
Timothy OBrien
701dda9a28 docs: Add documentation for Microsoft Azure AD SAML SSO (#4509) 2020-10-08 14:15:15 -07:00
Alexander Matyushentsev
8995d0405a fix: JS error on application list page if app has no namespace (#4499) 2020-10-07 11:41:21 -07:00
May Zhang
286f98ba82 feat: autosync protection (#4475)
* feat: autosync protection

* feat: autosync protection - update doc

* feat: autosync protection - update doc with version
2020-10-06 21:10:50 -07:00
Masaya Ozawa
10f68dde2d use resources (#4495)
https://kubernetes-sigs.github.io/kustomize/api-reference/kustomization/bases/
2020-10-06 16:25:01 -07:00
Tim Etchells
5592150f18 feat: Add version information panel (#4312) (#4376) 2020-10-06 12:03:25 -07:00
Keith Chong
7af7f30715 docs: Format issue in documentation site for dependencies.md (#4491) (#4496)
Signed-off-by: Keith Chong <kykchong@redhat.com>
2020-10-06 20:33:53 +02:00
Keith Chong
4534bd2725 docs: Add developer docs for gitops-engine changes (#4492)
Signed-off-by: Keith Chong <kykchong@redhat.com>
2020-10-06 19:31:57 +02:00
Chetan Banavikalmutt
95d19cdcca feat: Add labels to list view icons(#4396) (#4493)
The icons present in the list view should have labels similar to the icons in tree view

Fixes: #4396
2020-10-06 12:59:17 +02:00
Alexander
05b70f1e97 docs: add TableCheck to users (#4494)
Signed-off-by: Alexander Nicholson <4584443+DragonStuff@users.noreply.github.com>
2020-10-06 12:28:32 +02:00
Jean-François Roche
da57c9f1c8 docs: declare where to add the annotation (#4490)
We got bitten by thinking that this annotation could possibly added to the application to exclude all extraneous resources (see #4487).
The annotation must be added to the resource we wish to exclude.
2020-10-06 08:12:35 +02:00
Keith Chong
287e8cffdb refactor: fix: update gitops engine version (issue #3781) (#4489)
Signed-off-by: Keith Chong <kykchong@redhat.com>
2020-10-05 15:22:37 -07:00
Satoshi Inoue (butter)
f93da5346c docs: add VISITS Technologies to users (#4485) 2020-10-05 10:40:11 +02:00
Maxime Brunet
aefa739169 feat: Upgrade Dex to v2.25.0 (#4470) 2020-10-05 09:02:49 +02:00
k.bigwheel
3123c00a85 docs: Added Speee to USERS list (#4484) 2020-10-05 08:57:34 +02:00
Liviu Costea
efb7028d84 docs: Add instructions for cli installation on Windows #4481 (#4482)
* docs: Add instructions for cli installation on Windows

* docs: Remove chocolatey until it is part of the release
2020-10-05 08:53:33 +02:00
Isaac Gaskin
3848f64807 fix(info.go): omitting wildcard paths from controller cache (#4479)
edge case where /* should not be appended to path
2020-10-05 08:36:26 +02:00
Chance Zibolski
5af0c5ad3a fix: reposerver: Set GRPC max message size (#4472) 2020-10-02 13:53:29 -07:00
jannfis
24927c4d4f docs: Fix broken link to user guide (#4477) 2020-10-02 09:33:08 -07:00
John Pitman
91e62bfc3b fix: fix broken docker build of argocd-test-tools image (#4471)
Signed-off-by: John Pitman <jpitman@redhat.com>
2020-10-01 13:56:32 -07:00
Dewan Ahmed
b08f895d9a chore: removing circleci since we're moved to GitHub Actions (#4469) 2020-10-01 13:54:41 -07:00
dependabot[bot]
3409e0728f chore(deps): bump node-sass from 4.12.0 to 4.13.1 in /ui (#4267)
Bumps [node-sass](https://github.com/sass/node-sass) from 4.12.0 to 4.13.1.
- [Release notes](https://github.com/sass/node-sass/releases)
- [Changelog](https://github.com/sass/node-sass/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sass/node-sass/compare/v4.12.0...v4.13.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-10-01 01:08:44 -07:00
dependabot[bot]
dd63715017 chore(deps): bump http-proxy from 1.17.0 to 1.18.1 in /ui (#4286)
Bumps [http-proxy](https://github.com/http-party/node-http-proxy) from 1.17.0 to 1.18.1.
- [Release notes](https://github.com/http-party/node-http-proxy/releases)
- [Changelog](https://github.com/http-party/node-http-proxy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/http-party/node-http-proxy/compare/1.17.0...1.18.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-10-01 00:51:39 -07:00
dependabot[bot]
0d2fc86330 chore(deps): bump handlebars from 4.1.2 to 4.7.6 in /ui (#4271)
Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.1.2 to 4.7.6.
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/handlebars-lang/handlebars.js/blob/master/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.1.2...v4.7.6)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-10-01 00:49:34 -07:00
Alexander Matyushentsev
8299e99049 refactor: upgrade dependencies and node version (#4461) 2020-09-30 23:02:48 -07:00
John Pitman
7b60548e8a fix: switch monkeypatching implementation (#4462)
Signed-off-by: John Pitman <jpitman@redhat.com>
2020-09-30 14:02:18 -07:00
Jacek Wysocki
0226190ef4 docs: Add kinguin to USERS.md (#4458) 2020-09-30 13:54:57 -07:00
Carson A
25823a4625 feat: Add external link annotation support (#4380)
Example implementation of https://github.com/argoproj/argo-cd/issues/3487
2020-09-30 07:39:10 +02:00
May Zhang
712df19fac feat: add labels for projects (#4446)
* add labels for projects

* feat: add labels for projects

* feat: add labels for projects
2020-09-29 21:35:31 -07:00
jmpcyc
bccaefdac9 delete duplicate code (#4449) 2020-09-29 21:29:33 -07:00
Tim Etchells
53d50df001 fix: prevent sync error overflowing (#4453)
Also make the 'sync status' help icon clickable to view sync status

Signed-off-by: Tim Etchells <tetchell@redhat.com>
2020-09-29 17:33:37 -07:00
Alexander Matyushentsev
8b2e05c20d fix: remove redundant check from EditablePanel control (#4436) 2020-09-28 08:51:07 -07:00
Alexander Matyushentsev
9bb9c19e67 fix: show error message if unable to terminate operation (#4437) 2020-09-28 08:50:50 -07:00
Alexander Matyushentsev
6b106768a5 refactor: update gitops engine version (issues #4329, #4298) (#4434) 2020-09-25 16:09:09 -07:00
Josh Soref
4810874348 feat(ui): widgets don't have tooltips (#4257) (#4422)
Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-09-25 09:50:19 -07:00
Alexander Matyushentsev
d96083c293 fix: bump cache version due to redis compression feature (#4421) 2020-09-25 09:44:34 -07:00
Alexander Matyushentsev
f815c96605 docs: add 'helm.parameters.forceString' example to application.yaml (#4431) 2020-09-25 09:42:34 -07:00
Remington Breeze
4347a3c0ad feat(ui): Migrate project summary settings to EditablePanel for parity with rest of UI (#4400) 2020-09-24 13:40:03 -07:00
Josh Soref
3f7d60018f feat(ui): widgets don't have tooltips (#4257) (#4261) 2020-09-24 13:29:57 -07:00
Keith Chong
b7c2002a11 feat: Progress indicators in Argo CD UI (#4227) (#4411)
Signed-off-by: Keith Chong <kykchong@redhat.com>
2020-09-24 09:16:32 -07:00
Sayak Mukhopadhyay
c1ee89b502 docs: Changed docs for Helm to clarify the location of values files. (#4417)
Also did a grammar pass.
2020-09-24 10:36:44 +02:00
Viktor Farcic
74f5043e87 docs: 20 min video (#4390) 2020-09-23 17:22:52 -07:00
May Zhang
fbfa89d358 fix: Support transition from a git managed namespace to auto create (#4401)
* fix: Support transition from a git managed namespace to auto create

* fix: Support transition from a git managed namespace to auto create
2020-09-22 17:38:55 -07:00
Josh Soref
1c95c90a2d fix(ui): center icon tiles in list view (#4262) (#4266) 2020-09-22 13:04:42 -07:00
bukbuk1231
9f47a11621 feat: display k8s object CREATED_AT time in local time instead of UTC (#4347) (#4383)
* feat: display k8s object CREATED_AT time in local time instead of UTC

* fix linting issues
2020-09-21 16:28:05 -07:00
Chetan Banavikalmutt
e28a3e5ed0 fix: Cluster remove should return an error for unknown cluster name (#4366) 2020-09-21 13:56:21 -07:00
Johan Sandström
52cae98705 fix: add no log init flag to useradd (#4379)
This commit fixes an error where the underlaying disk would get
filled up when running make test-tools-image and the user running
it are running with a big UID.

Adding --no-log-init or -l will prevent useradd from trying to make
sure that there are is room for the user in lastlog and faillog.
2020-09-21 10:58:24 +02:00
Alexander Matyushentsev
3850e80040 fix: swagger UI stuck loading (#4377) 2020-09-18 15:39:56 -07:00
Jonathan West
989f5c80c6 fix: Sync Window status in argocd CLI is not matched with argocd UI for same sync window (#3831) (#4372) 2020-09-18 23:08:25 +02:00
Sayak Mukhopadhyay
82340a0740 Fixed padding issues with application list in cards and row view (#4364) 2020-09-18 10:38:03 -07:00
Alexander Matyushentsev
28e60406a8 fix: prevent 'argocd app sync' hangs if sync is completed too quickly (#4373) 2020-09-17 16:21:25 -07:00
Alex Collins
eb0d018c31 chore: Upgrade to pkg v0.2.0 (#4356) 2020-09-17 14:05:27 -07:00
William Tam
850de2021a Add documentation checkboxes in PR template (#4371) 2020-09-17 12:58:31 -07:00
Josh Soref
b8d1b9bbc0 fix: Reword application is healthy (#4336) (#4357)
Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-09-17 21:33:30 +02:00
John Pitman
7caa2106ef fix: 'prettier' error from tslint (#4368)
Signed-off-by: John Pitman <jpitman@redhat.com>
2020-09-17 21:32:26 +02:00
Herrmann Hinz
24b5c1e34d Update app_deletion.md (#4365)
kubectl command typo
2020-09-17 09:01:29 -07:00
Keith Chong
3aa0748c70 fix: Add tooltip for application tile detail labels (#4341) (#4343)
* [4341] Add tooltip for application tile labels

* fix: Add tooltip for application tile detail labels (#4341)

Signed-off-by: Keith Chong <kykchong@redhat.com>
2020-09-16 19:28:05 -07:00
Josh Soref
1462ab3c06 Improve release docs (#4346)
Mention bumping `VERSION` on master

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-09-16 19:09:55 -07:00
Alexander Matyushentsev
5de3a302fb fix: argocd app wait/sync might stuck (#4350) 2020-09-16 19:03:48 -07:00
jheyduk
14fa7f954c fix: application external url falsely uses backend servicePort (#4339)
* fix: application external url falsely uses backend servicePort #2318

Use https if spec.tls is specified for the related host. Otherwise use http

* refactor / add new test
2020-09-16 16:18:28 -07:00
Alexander Matyushentsev
1b3d7a02e1 fix: failed syncs are not retried soon enough (#4353) 2020-09-16 13:39:18 -07:00
Jonathan West
eb13305984 fix: Log output stops if message is too long (#3258) (#4334) 2020-09-15 14:10:52 -07:00
Josh Soref
3a30a4fc74 chore: change version to 1.8.0 (#4340)
Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-09-15 13:57:35 -07:00
John Pitman
d4ddd51602 fix: create valid spec for project policy (#4332)
Signed-off-by: John Pitman <jpitman@redhat.com>
2020-09-15 11:07:38 -07:00
Isaac Gaskin
95eda65759 fix(applications-table.tsx): adding "name" to Cluster (#4317)
this corrects the "destination" of the table view of applications if they use
app.spec.destination.name instead of app.spec.destination.server
2020-09-15 11:03:07 -07:00
Gagan Hegde
c26573369d Add status badge for project #4001 (#4301)
* The status badge for the project has been added : //Sample url: http://localhost:8080/api/badge?project=default, creates a badge with the health and sync status
2020-09-15 09:55:32 -07:00
Josh Soref
c91acc0673 chore: spelling: will (#4292)
Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-09-15 09:54:23 -07:00
Joost VdC
51a5795b44 Update understand_the_basics.md (#4299)
link to kubernetes course is outdated
2020-09-15 09:54:12 -07:00
William Tam
9aae99cf7f fix: Only processes need root uid will run as root inside test container (#4210)
* Add  "chown" to gpg/keys  in "start-local" target that so that repo-server can access gpg keys.

* * Set -u uid:gid in the docker run commands  so that test images are run under the current user.
* test Procfile processes will not need to perform "su"  to  default user (which has the current user's uid/gid)

* Remove chown in start-e2e-local

* clean up, remove  "bash -c"

* Test containers are run as uid 0 which allows uid_entrypoint.sh  to perform some user setup.  uid_entrypoint.sh creates a non-root user (default) and enables passwordless sudo for that user.    The container entry point command is run as the non-root user.   "goreman start"  does "sudo" to to the processes that need root permission including sshd, fcgiwrap, and nginix.   The other processes are running as the non-root user.

* use /bin/bash

* change back to sh

* Docker image to create  unpriveleged testuser and enable passwordless sudo for that user
2020-09-15 11:32:16 +02:00
Chetan Banavikalmutt
5f680d6cec fix: Log an error when invalid token exists (#4318)
Instead of returning an error for invalid token and thereby breaking API requests for all users, print the error to the logs.

Fixes: #4156
2020-09-14 23:18:51 -07:00
Tim Etchells
28a76352c5 fix: Add tooltips to sync result table (#4310)
Bug #4234

Signed-off-by: Tim Etchells <tetchell@redhat.com>
2020-09-14 17:45:14 -07:00
May Zhang
cfb925c0d4 fix: app create with -f should not ignore other options (#4322) 2020-09-14 15:29:45 -07:00
Isaac Gaskin
4e6d8cc1d2 fix(logging.go): changing marshaler for JSON logging to use gogo (#4319)
* fix(logging.go): changing marshaler for JSON logging to use gogo

grpc-gateway json marshaler breaks with gogo protos

#4117

* Retrigger CI pipeline
2020-09-14 15:15:31 -07:00
Alexander Matyushentsev
f215233af4 fix: limit concurrent list requests accross all clusters (#4328) 2020-09-14 14:45:02 -07:00
Chance Zibolski
b1e3036bc2 chore: Update helm3 to 3.3.1 (#4323) 2020-09-14 14:28:33 -07:00
Alexander Matyushentsev
df4002a987 fix: fix flaky TestTrackAppStateAndSyncApp e2e test (#4325) 2020-09-14 11:45:59 -07:00
Tim Etchells
96035d3b51 fix: Bundle google font into webpack build (#4324)
Signed-off-by: Tim Etchells <tetchell@redhat.com>
2020-09-14 10:35:17 -07:00
Alexander Matyushentsev
89ee234634 fix: fix possible deadlock in /v1/api/stream/applications and /v1/api/application APIs (#4315) 2020-09-11 20:39:58 -07:00
Alexander Matyushentsev
ff2aa41539 fix: WatchResourceTree does not enforce RBAC (#4311) 2020-09-11 11:14:08 -07:00
Alexander Matyushentsev
85b27e6deb fix: app refresh API should use app resource version (#4303) 2020-09-11 10:26:45 -07:00
Alexander Matyushentsev
d60486fb47 fix: use redis pub-sub with correct context (#4300) 2020-09-10 09:57:32 -07:00
jannfis
41ca6b2ada chore: Upgrade installation manifests & codegen to Kustomize 3 (#3347)
* Use kustomize v3 to build ArgoCD manifests

* Update to test-tools-image v0.3.0

* Reorder patches so Kustomize v3 will properly find targets

* adding back these changes

* Use the generated files.

* changed the namespace

* changed kustomize version to 3.8.1 to be in sync with the one in tool-version.sh

* revert changes in makefile

* Re-run codegen

Co-authored-by: Zhang <may_zhang@intuit.com>
Co-authored-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2020-09-09 15:31:33 -07:00
Alexander Matyushentsev
d9f4e224a0 fix: use informer instead of k8s watch to ensure app is refreshed (#4290) 2020-09-09 13:08:18 -07:00
Naoto Enokawa
29c1095a2d docs: Fix typo (#4287) 2020-09-09 08:40:59 -07:00
William Tam
0473ed8104 doc: fix typo "make lint-ui" (#4283)
There is no target "ui" in the Makefile.   The documentation should read "lint-ui".
2020-09-08 13:28:06 -07:00
Joseph Petersen
706205958c fix containers syntax (#4277) 2020-09-08 13:06:25 -07:00
Hyungrok Kim
e5dde6eefc docs: Add Toss to USERS.md (#4282) 2020-09-08 12:19:17 -07:00
Sayak Mukhopadhyay
aa2762a9b7 fix: Added cluster authentication to AKS clusters (#4265) 2020-09-07 19:53:33 -07:00
Niketh Sabbineni
6959f032f0 Update USERS.md (#4274)
Added sumologic to the list of companies
2020-09-05 10:33:07 -07:00
Alexander Matyushentsev
1dfe670d1c fix: revert accidental changes from PR #4250 (#4263) 2020-09-04 16:02:36 -07:00
Alexander Matyushentsev
76782a0270 fix: automatically stop watch API requests when page is hidden (#4269) 2020-09-04 14:36:47 -07:00
dherman
b3be910465 perf: upgrade redis deps to enable compression (#4259)
Newer versions of the redis go libraries include built-in support for
compression of values going to and from redis.  Since the controller
is extremely chatty and makes heavy use of redis for caching, this
reduces the amount of bandwidth requires significantly.

This change should be backwards compatible since the redis libraries
detect whether or not compression was used and does not error in the
no compression case.

Fixes #4256
2020-09-04 13:59:59 -07:00
Alexander Matyushentsev
34c4aa42d6 fix: upgrade gitops-engine dependency (issues #4242, #1881) (#4268) 2020-09-04 13:58:56 -07:00
Alexander Matyushentsev
89ebf5a906 fix: application stream API should not return 'ADDED' events if resource version is provided (#4260) 2020-09-03 22:13:49 -07:00
Alexander Zigelski
bae5f93590 Migrate to Homebrew Core (#3978) (#3979)
* Update Homebrew formula instructions and CI

* docs: Migrate homebrew formula to homebrew-core
2020-09-03 14:44:55 -07:00
Tianxin Dong
8f7eabefd4 docs: Fix typo in documentation (#4254) 2020-09-03 14:03:48 -07:00
Takahiro Tsuruda
8fa2c7f43f docs: add Nikkei to user list (#4253)
Signed-off-by: TakahiroTsuruda <isrgnoe@gmail.com>
2020-09-03 14:02:41 -07:00
Rael Garcia
d3eb8e9590 docs: add list format to the cluster namespaces field (#4237) 2020-09-03 14:01:57 -07:00
Mikhail Mazurskiy
ad38421b76 fix: return parsing error (#3942)
Don't assume that a file is not a Kubernetes
resource if there was no previous objects parsed
2020-09-03 10:14:18 -07:00
Alexander Matyushentsev
92bbcf15e8 fix: JS error when using cluster filter in the /application view (#4247) 2020-09-03 10:13:52 -07:00
Josh Soref
10dc3ac12a fix: Pending deletion tooltip includes trailing semicolon ; (#4250) (#4251) 2020-09-02 21:43:50 -07:00
Alexander Matyushentsev
2f5e45490c fix: improve applications list page client side performance (#4244) 2020-09-02 15:37:37 -07:00
James Callahan
e10c20f683 chore: update to kustomize 3.8.1 (#4130) 2020-09-02 14:01:35 -07:00
Shoubhik Bose
bc537c1e87 docs: improve serviceability terminology (#4240) 2020-09-02 11:18:24 -07:00
Dan Garfield
a482546112 Add CNCF talk to list of resources (#4241)
This is a talk Codefresh did with the CNCF in July, it's been one of the most popular talks on their platform (top 10 for august). Huzzah!
2020-09-02 11:17:31 -07:00
Alexander Matyushentsev
7ead93458e fix: application details page crash when app is deleted (#4229) 2020-09-01 15:25:13 -07:00
May Zhang
dc6d88950c Doc: updated doc for namespace-install (#4199) 2020-09-01 15:08:36 -07:00
Darshan Chaudhary
c44074d4d6 fix: allow clusters to include resources independently (#4027)
Signed-off-by: darshanime <deathbullet@gmail.com>
2020-09-01 14:06:06 -07:00
Alexander Matyushentsev
016c8b333a fix: api-server unnecessary normalize projects on every start (#4219) 2020-09-01 13:06:11 -07:00
Alexander Matyushentsev
cc1592eb0c refactor: load only project names in UI (#4217) 2020-09-01 12:50:03 -07:00
jannfis
ea9b0b35d0 fix: Re-create already initialized ARGOCD_GNUPGHOME on startup (#4214) (#4223) 2020-09-01 21:01:56 +02:00
Josh Soref
44623d6be2 fix: vertically center 3+ letter abbreviations (#4179) (#4180) 2020-09-01 10:39:01 -07:00
Darshan Chaudhary
f625ddc6b9 feat: allow diff against a revision (#4205)
Signed-off-by: darshanime <deathbullet@gmail.com>
2020-09-01 10:30:37 -07:00
chrisob
324a336a52 fix: Add openshift as a dex connector type which requires a redirectURI (#4222) 2020-09-01 10:27:38 -07:00
Alexander Matyushentsev
d09bd23cf8 feat: support gzip compression in api server (#4218) 2020-09-01 10:08:08 -07:00
Johan Sandström
90eb262f64 docs: document how to set helm version (#4216) 2020-09-01 10:02:35 -07:00
Mickaël Canévet
c14f87d565 Update USERS.md (#4213) 2020-08-31 11:24:09 -07:00
Alexander Matyushentsev
fca0f69b5e refactor: Replace status.observedAt with redis pub/sub channels for resource tree updates (#1340) (#4208) 2020-08-31 10:18:12 -07:00
Maxime Brunet
edf2904004 feat: Add Jsonnet version to version endpoint (#4207) 2020-08-31 10:29:32 +02:00
Maxime Brunet
d7a70bfc6f fix(cli): Fix local diff/sync of apps using cluster name (#4201)
This fixes the cluster query when the application uses cluster name as destination:

```shell
$ argocd app diff guestbook --local=guestbook/
FATA[0010] rpc error: code = Internal desc = runtime error: invalid memory address or nil pointer dereference
```
2020-08-29 09:56:49 +02:00
faruryo
a4ea2624a8 docs: Add documentation for Namespace Auto-Creation in application.yaml (#4203)
* update docs/application.yaml for Namespace Auto-Creation

* remove trailing spaces in application.yaml
2020-08-29 09:50:55 +02:00
Alexander Matyushentsev
dec73c77e6 fix: cache inconsistency of child resources (#4053) (#4202) 2020-08-28 22:41:28 -07:00
Alexander Matyushentsev
761ad0bdcf docs: add 1.7.1 and 1.7.2 to CHANGELOG (#4197) 2020-08-28 13:26:15 -07:00
Chetan Banavikalmutt
d1e272e192 feat: Flag to add kustomize common labels (#4131) 2020-08-28 12:07:19 -07:00
Shea Stewart
9fb7aa4f20 Update USERS.md (#4193)
I am a partner at Arctiq and we use the argo suite extensively for internal tooling, and am also the technical lead for the DevOps Platform at BCGov in which we are using ArgoCD (and the suite) for cluster configuration management.
2020-08-28 09:07:50 -07:00
Josh Soref
8fa0f04e43 fix: remove stray period (#4183) (#4184) 2020-08-27 16:58:42 -07:00
Byungjin Park (BJ)
c00e84700c Update USERS.md (#4185) 2020-08-27 16:52:28 -07:00
Alexander Matyushentsev
2d2335f95a fix: upgrade github.com/evanphx/json-patch to v4.9.0 (#4189) 2020-08-27 15:21:57 -07:00
Daisuke Taniwaki
10d05cdb60 fix: Allow no config cluster (#4164)
* Update USERS.md

* Allow no config cluster

* Add tests

* Fix lint issues
2020-08-26 19:50:02 -07:00
Michael Barrientos
9f79340505 fix: support for PKCE for cli login (#2932) (#4067) 2020-08-26 16:02:39 -07:00
Alexander Matyushentsev
290712d4b3 docs: document YAML handling change in v1.7 upgrade instructions (#4168) 2020-08-26 14:41:08 -07:00
Alexander Matyushentsev
c49dd8d383 feat: support storing app parameters in .argocd-app.yaml file (#4084)
* feat: support storing app parameters in .argocd-app.yaml file

* rename .argocd-app.yaml to .argocd-source.yaml
2020-08-26 14:27:34 -07:00
Oleg Sucharevich
3a50f8df81 feat: do not include kube-api check in application liveness flow (#4163)
* feat: do not include kube-api liveness check in application liveness flow
2020-08-26 10:33:36 -07:00
Alexander Matyushentsev
569a2a6bc6 fix: Unable to create project JWT token on K8S v1.15 (#4165) 2020-08-26 10:31:52 -07:00
Alex Gervais
2e8a8f09b1 docs: Update README.md with a new community tutorial (#4161) 2020-08-26 16:04:34 +02:00
Alexander Matyushentsev
d04b6e2d35 refactor: upgrade gitops-engine version (#4160) 2020-08-26 00:00:28 -07:00
Johan
f208700f78 feat: Add configurable Helm version (#4111)
This commit adds support for configurable Helm version either via
declarative syntax or via argocd cli.

New helm option 'Version' added to the ApplicationSourceHelm
struct which can be either 'v2' or 'v3'.

Argocd app create accepts '--helm-version' that also looks for the
same Helm versions as above.
2020-08-25 12:36:27 -07:00
Chetan Banavikalmutt
7a3d05cb7c docs: Switch to argocd namespace before starting local services (#4153) 2020-08-25 19:20:06 +02:00
May Zhang
8df8bfff18 fix: Badge links are not generating properly when using --rootpath (#4140)
* fix: Badge links are not generating properly when using --rootpath

* fix: fix lint error

* fix: use context.baseHref
2020-08-25 09:54:30 -07:00
Josh Soref
9ac6bb3248 fix: clear filters to show (#4142) (#4143) 2020-08-25 09:20:36 -07:00
Alexander Matyushentsev
ee57ded16f refactor: upgrade K8S client to v0.18.8 (#4149) 2020-08-24 16:46:38 -07:00
Shoubhik Bose
f508dec107 chore: add nodemodules to gitignore (#4151) 2020-08-24 14:00:22 -07:00
S.H
0b387a454b doc: fix the ci_automation's kustomize source code. (#4144) 2020-08-24 13:57:54 -07:00
May Zhang
bc565d384d fix: UI setting auto sync causes erroneous config (#4118)
* fix: UI setting auto sync causes erroneous config

* fix: remove log
2020-08-24 12:13:06 -07:00
jannfis
372eae0f21 fix: Make GnuPG keyring independent of user ID within container (#4136)
* fix: Make GnuPG keyring independent of user ID within container

* Update unit test
2020-08-24 12:01:41 -07:00
Marc Boorshtein
474301c5ab docs: fixed link for OpenUnison (#4150)
* added docs for openunison integration

* fixed formatting

* added link to makedocs, fix last header

* fix link
2020-08-24 11:58:39 -07:00
Marc Boorshtein
beb2817d6f docs: added docs for openunison integration (#4146)
* added docs for openunison integration

* fixed formatting

* added link to makedocs, fix last header
2020-08-24 19:28:51 +02:00
Remington Breeze
0ee983fc31 chore(docs): add note about base64 encoding to SSO documentation (#4139) 2020-08-24 16:43:57 +02:00
Oleg Sucharevich
c32d5fd5ee docs: Add remote debugging example to contribution docs
docs: fix grammar

Minor editing

empty
2020-08-24 16:39:51 +02:00
Yankee
0d193cfd57 docs: Update broken link for Branch Tracking (#4147)
Updated broken internal link for the Branch Tracking section.
2020-08-24 16:35:33 +02:00
Povilas Versockas
263e7a8497 feat: add grpc metrics in repo server (#3827) 2020-08-18 21:48:42 -07:00
Povilas Versockas
7f86e6b38c feat: add git histogram metrics (#3828) 2020-08-18 21:48:00 -07:00
Povilas Versockas
194d471db4 fix: rename redis histogram metric name (#3829) 2020-08-18 21:45:47 -07:00
Jared Welch
84e8af7976 chore: Add New Relic to USERS.md (#4119)
New Relic (https://newrelic.com/) is officially using ArgoCD along with the other Argo projects to deploy to Kubernetes.
2020-08-18 15:08:17 -07:00
Nicholas St. Germain
f2dca0315c docs: Update traefik gRPC docs (#4112) (#4113) 2020-08-18 13:35:04 -07:00
Suraj Biyani
01d9b94f62 docs: Add Beat to USERS (#4109) 2020-08-17 13:56:27 -07:00
Oleg Sucharevich
ebb216ff11 fix: log warn when external url is not fully qualified (#4108) 2020-08-17 14:54:10 +02:00
Chetan Banavikalmutt
e56997f504 docs: Fix spelling in contributing guide and FAQ (#4107) 2020-08-17 09:06:52 +02:00
606 changed files with 34872 additions and 11074 deletions

View File

@@ -1,16 +0,0 @@
version: 2.1
jobs:
dummy:
docker:
- image: cimg/base:2020.01
steps:
- run:
name: Dummy step
command: |
echo "This is a dummy step to satisfy CircleCI"
workflows:
version: 2
workflow:
jobs:
- dummy

View File

@@ -1,324 +0,0 @@
# CircleCI currently disabled in favor of GH actions
version: 2.1
commands:
prepare_environment:
steps:
- run:
name: Configure environment
command: |
set -x
echo "export GOCACHE=/tmp/go-build-cache" | tee -a $BASH_ENV
echo "export ARGOCD_TEST_VERBOSE=true" | tee -a $BASH_ENV
echo "export ARGOCD_TEST_PARALLELISM=4" | tee -a $BASH_ENV
echo "export ARGOCD_SONAR_VERSION=4.2.0.1873" | tee -a $BASH_ENV
configure_git:
steps:
- run:
name: Configure Git
command: |
set -x
# must be configured for tests to run
git config --global user.email you@example.com
git config --global user.name "Your Name"
echo "export PATH=/home/circleci/.go_workspace/src/github.com/argoproj/argo-cd/hack:\$PATH" | tee -a $BASH_ENV
echo "export GIT_ASKPASS=git-ask-pass.sh" | tee -a $BASH_ENV
setup_go_modules:
steps:
- run:
name: Run go mod download and populate vendor
command: |
go mod download
go mod vendor
save_coverage_info:
steps:
- persist_to_workspace:
root: .
paths:
- coverage.out
save_node_modules:
steps:
- persist_to_workspace:
root: ~/argo-cd
paths:
- ui/node_modules
save_go_cache:
steps:
- persist_to_workspace:
root: /tmp
paths:
- go-build-cache
attach_go_cache:
steps:
- attach_workspace:
at: /tmp
install_golang:
steps:
- run:
name: Install Golang v1.14.1
command: |
go get golang.org/dl/go1.14.1
[ -e /home/circleci/sdk/go1.14.1 ] || go1.14.1 download
go env
echo "export GOPATH=/home/circleci/.go_workspace" | tee -a $BASH_ENV
echo "export PATH=/home/circleci/sdk/go1.14.1/bin:\$PATH" | tee -a $BASH_ENV
jobs:
build:
docker:
- image: argoproj/argocd-test-tools:v0.5.0
working_directory: /go/src/github.com/argoproj/argo-cd
steps:
- prepare_environment
- checkout
- run: make build-local
- run: chmod -R 777 vendor
- run: chmod -R 777 ${GOCACHE}
- save_go_cache
codegen:
docker:
- image: argoproj/argocd-test-tools:v0.5.0
working_directory: /go/src/github.com/argoproj/argo-cd
steps:
- prepare_environment
- checkout
- attach_go_cache
- run: helm2 init --client-only
- run: make codegen-local
- run:
name: Check nothing has changed
command: |
set -xo pipefail
# This makes sure you ran `make pre-commit` before you pushed.
# We exclude the Swagger resources; CircleCI doesn't generate them correctly.
# When this fails, it will, create a patch file you can apply locally to fix it.
# To troubleshoot builds: https://argoproj.github.io/argo-cd/developer-guide/ci/
git diff --exit-code -- . ':!Gopkg.lock' ':!assets/swagger.json' | tee codegen.patch
- store_artifacts:
path: codegen.patch
destination: .
test:
working_directory: /go/src/github.com/argoproj/argo-cd
docker:
- image: argoproj/argocd-test-tools:v0.5.0
steps:
- prepare_environment
- checkout
- configure_git
- attach_go_cache
- run: make test-local
- run:
name: Uploading code coverage
command: bash <(curl -s https://codecov.io/bash) -f coverage.out
- run:
name: Output of test-results
command: |
ls -l test-results || true
cat test-results/junit.xml || true
- save_coverage_info
- store_test_results:
path: test-results
- store_artifacts:
path: test-results
destination: .
lint:
working_directory: /go/src/github.com/argoproj/argo-cd
docker:
- image: argoproj/argocd-test-tools:v0.5.0
steps:
- prepare_environment
- checkout
- configure_git
- attach_vendor
- store_go_cache_docker
- run:
name: Run golangci-lint
command: ARGOCD_LINT_GOGC=10 make lint-local
- run:
name: Check that nothing has changed
command: |
gDiff=$(git diff)
if test "$gDiff" != ""; then
echo
echo "###############################################################################"
echo "golangci-lint has made automatic corrections to your code. Please check below"
echo "diff output and commit this to your local branch, or run make lint locally."
echo "###############################################################################"
echo
git diff
exit 1
fi
sonarcloud:
working_directory: /go/src/github.com/argoproj/argo-cd
docker:
- image: argoproj/argocd-test-tools:v0.5.0
environment:
NODE_MODULES: /go/src/github.com/argoproj/argo-cd/ui/node_modules
steps:
- prepare_environment
- checkout
- attach_workspace:
at: .
- run:
command: mkdir -p /tmp/cache/scanner
name: Create cache directory if it doesn't exist
- restore_cache:
keys:
- v1-sonarcloud-scanner-4.2.0.1873
- run:
command: |
set -e
VERSION=4.2.0.1873
SONAR_TOKEN=$SONAR_TOKEN
SCANNER_DIRECTORY=/tmp/cache/scanner
export SONAR_USER_HOME=$SCANNER_DIRECTORY/.sonar
OS="linux"
echo $SONAR_USER_HOME
if [[ ! -x "$SCANNER_DIRECTORY/sonar-scanner-$VERSION-$OS/bin/sonar-scanner" ]]; then
curl -Ol https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$VERSION-$OS.zip
unzip -qq -o sonar-scanner-cli-$VERSION-$OS.zip -d $SCANNER_DIRECTORY
fi
chmod +x $SCANNER_DIRECTORY/sonar-scanner-$VERSION-$OS/bin/sonar-scanner
chmod +x $SCANNER_DIRECTORY/sonar-scanner-$VERSION-$OS/jre/bin/java
# Workaround for a possible bug in CircleCI
if ! echo $CIRCLE_PULL_REQUEST | grep https://github.com/argoproj; then
unset CIRCLE_PULL_REQUEST
unset CIRCLE_PULL_REQUESTS
fi
# Explicitly set NODE_MODULES
export NODE_MODULES=/go/src/github.com/argoproj/argo-cd/ui/node_modules
export NODE_PATH=/go/src/github.com/argoproj/argo-cd/ui/node_modules
$SCANNER_DIRECTORY/sonar-scanner-$VERSION-$OS/bin/sonar-scanner
name: SonarCloud
- save_cache:
key: v1-sonarcloud-scanner-4.2.0.1873
paths:
- /tmp/cache/scanner
e2e:
working_directory: /home/circleci/.go_workspace/src/github.com/argoproj/argo-cd
machine:
image: ubuntu-1604:201903-01
environment:
ARGOCD_FAKE_IN_CLUSTER: "true"
ARGOCD_SSH_DATA_PATH: "/tmp/argo-e2e/app/config/ssh"
ARGOCD_TLS_DATA_PATH: "/tmp/argo-e2e/app/config/tls"
ARGOCD_E2E_K3S: "true"
steps:
- run:
name: Install and start K3S v0.5.0
command: |
curl -sfL https://get.k3s.io | sh -
sudo chmod -R a+rw /etc/rancher/k3s
kubectl version
environment:
INSTALL_K3S_EXEC: --docker
INSTALL_K3S_VERSION: v0.5.0
- prepare_environment
- checkout
- run:
name: Fix permissions on filesystem
command: |
mkdir -p /home/circleci/.go_workspace/pkg/mod
chmod -R 777 /home/circleci/.go_workspace/pkg/mod
mkdir -p /tmp/go-build-cache
chmod -R 777 /tmp/go-build-cache
- attach_go_cache
- run:
name: Update kubectl configuration for container
command: |
ipaddr=$(ifconfig $IFACE |grep "inet " | awk '{print $2}')
if echo $ipaddr | grep -q 'addr:'; then
ipaddr=$(echo $ipaddr | awk -F ':' '{print $2}')
fi
test -d $HOME/.kube || mkdir -p $HOME/.kube
kubectl config view --raw | sed -e "s/127.0.0.1:6443/${ipaddr}:6443/g" -e "s/localhost:6443/${ipaddr}:6443/g" > $HOME/.kube/config
environment:
IFACE: ens4
- run:
name: Start E2E test server
command: make start-e2e
background: true
environment:
DOCKER_SRCDIR: /home/circleci/.go_workspace/src
ARGOCD_E2E_TEST: "true"
ARGOCD_IN_CI: "true"
GOPATH: /home/circleci/.go_workspace
- run:
name: Wait for API server to become available
command: |
count=1
until curl -v http://localhost:8080/healthz; do
sleep 10;
if test $count -ge 60; then
echo "Timeout"
exit 1
fi
count=$((count+1))
done
- run:
name: Run E2E tests
command: |
make test-e2e
environment:
ARGOCD_OPTS: "--plaintext"
ARGOCD_E2E_K3S: "true"
IFACE: ens4
DOCKER_SRCDIR: /home/circleci/.go_workspace/src
GOPATH: /home/circleci/.go_workspace
- store_test_results:
path: test-results
- store_artifacts:
path: test-results
destination: .
ui:
docker:
- image: node:11.15.0
working_directory: ~/argo-cd/ui
steps:
- checkout:
path: ~/argo-cd/
- restore_cache:
keys:
- yarn-packages-v4-{{ checksum "yarn.lock" }}
- run: yarn install --frozen-lockfile --ignore-optional --non-interactive
- save_cache:
key: yarn-packages-v4-{{ checksum "yarn.lock" }}
paths: [~/.cache/yarn, node_modules]
- run: yarn test
- run: ./node_modules/.bin/codecov -p ..
- run: NODE_ENV='production' yarn build
- run: yarn lint
- save_node_modules
orbs:
sonarcloud: sonarsource/sonarcloud@1.0.1
workflows:
version: 2
workflow:
jobs:
- build
- test:
requires:
- build
- codegen:
requires:
- build
- ui:
requires:
- build
- sonarcloud:
context: SonarCloud
requires:
- test
- ui
- e2e:
requires:
- build

View File

@@ -6,8 +6,7 @@ labels: 'bug'
assignees: ''
---
If you are trying to resolve an environment-specific issue or have a one-off question about the edge case that does not require a feature then please consider asking a
question in argocd slack [channel](https://argoproj.github.io/community/join-slack).
If you are trying to resolve an environment-specific issue or have a one-off question about the edge case that does not require a feature then please consider asking a question in argocd slack [channel](https://argoproj.github.io/community/join-slack).
Checklist:

View File

@@ -3,5 +3,7 @@ Checklist:
* [ ] Either (a) I've created an [enhancement proposal](https://github.com/argoproj/argo-cd/issues/new/choose) and discussed it with the community, (b) this is a bug fix, or (c) this does not need to be in the release notes.
* [ ] The title of the PR states what changed and the related issues number (used for the release note).
* [ ] I've updated both the CLI and UI to expose my feature, or I plan to submit a second PR with them.
* [ ] Does this PR require documentation updates?
* [ ] I've updated documentation as required by this PR.
* [ ] Optional. My organization is added to USERS.md.
* [ ] I've signed the CLA and my build is green ([troubleshooting builds](https://argoproj.github.io/argo-cd/developer-guide/ci/)).

View File

@@ -30,7 +30,7 @@ jobs:
- name: Setup Golang
uses: actions/setup-go@v1
with:
go-version: '1.14.2'
go-version: '1.14.12'
- name: Download all Go modules
run: |
go mod download
@@ -48,7 +48,7 @@ jobs:
- name: Setup Golang
uses: actions/setup-go@v1
with:
go-version: '1.14.2'
go-version: '1.14.12'
- name: Restore go build cache
uses: actions/cache@v1
with:
@@ -67,10 +67,10 @@ jobs:
- name: Checkout code
uses: actions/checkout@v2
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v1
uses: golangci/golangci-lint-action@v2
with:
version: v1.26
args: --timeout 5m
version: v1.29
args: --timeout 5m --exclude SA5011
test-go:
name: Run unit tests for Go packages
@@ -87,7 +87,7 @@ jobs:
- name: Setup Golang
uses: actions/setup-go@v1
with:
go-version: '1.14.2'
go-version: '1.14.12'
- name: Install required packages
run: |
sudo apt-get install git -y
@@ -99,9 +99,11 @@ jobs:
run: |
git fetch --prune --no-tags --depth=1 origin +refs/heads/*:refs/remotes/origin/*
- name: Add ~/go/bin to PATH
run: echo "::add-path::/home/runner/go/bin"
run: |
echo "/home/runner/go/bin" >> $GITHUB_PATH
- name: Add /usr/local/bin to PATH
run: echo "::add-path::/usr/local/bin"
run: |
echo "/usr/local/bin" >> $GITHUB_PATH
- name: Restore go build cache
uses: actions/cache@v1
with:
@@ -130,6 +132,61 @@ jobs:
name: test-results
path: test-results/
test-go-race:
name: Run unit tests with -race, for Go packages
runs-on: ubuntu-latest
needs:
- build-go
steps:
- name: Create checkout directory
run: mkdir -p ~/go/src/github.com/argoproj
- name: Checkout code
uses: actions/checkout@v2
- name: Create symlink in GOPATH
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
- name: Setup Golang
uses: actions/setup-go@v1
with:
go-version: '1.14.12'
- name: Install required packages
run: |
sudo apt-get install git -y
- name: Switch to temporal branch so we re-attach head
run: |
git switch -c temporal-pr-branch
git status
- name: Fetch complete history for blame information
run: |
git fetch --prune --no-tags --depth=1 origin +refs/heads/*:refs/remotes/origin/*
- name: Add ~/go/bin to PATH
run: |
echo "/home/runner/go/bin" >> $GITHUB_PATH
- name: Add /usr/local/bin to PATH
run: |
echo "/usr/local/bin" >> $GITHUB_PATH
- name: Restore go build cache
uses: actions/cache@v1
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
- name: Install all tools required for building & testing
run: |
make install-test-tools-local
- name: Setup git username and email
run: |
git config --global user.name "John Doe"
git config --global user.email "john.doe@example.com"
- name: Download and vendor all required packages
run: |
go mod download
- name: Run all unit tests
run: make test-race-local
- name: Generate test results artifacts
uses: actions/upload-artifact@v2
with:
name: race-results
path: test-results/
codegen:
name: Check changes to generated code
runs-on: ubuntu-latest
@@ -139,15 +196,17 @@ jobs:
- name: Setup Golang
uses: actions/setup-go@v1
with:
go-version: '1.14.2'
go-version: '1.14.12'
- name: Create symlink in GOPATH
run: |
mkdir -p ~/go/src/github.com/argoproj
cp -a ../argo-cd ~/go/src/github.com/argoproj
- name: Add /usr/local/bin to PATH
run: echo "::add-path::/usr/local/bin"
- name: Add ~/go/bin to PATH
run: echo "::add-path::/home/runner/go/bin"
run: |
echo "/home/runner/go/bin" >> $GITHUB_PATH
- name: Add /usr/local/bin to PATH
run: |
echo "/usr/local/bin" >> $GITHUB_PATH
- name: Download & vendor dependencies
run: |
# We need to vendor go modules for codegen yet
@@ -184,7 +243,7 @@ jobs:
- name: Setup NodeJS
uses: actions/setup-node@v1
with:
node-version: '11.15.0'
node-version: '12.18.4'
- name: Restore node dependency cache
id: cache-dependencies
uses: actions/cache@v1
@@ -274,6 +333,9 @@ jobs:
test-e2e:
name: Run end-to-end tests
runs-on: ubuntu-latest
strategy:
matrix:
k3s-version: [v1.19.2, v1.18.9, v1.17.11, v1.16.15]
needs:
- build-go
env:
@@ -292,10 +354,10 @@ jobs:
- name: Setup Golang
uses: actions/setup-go@v1
with:
go-version: '1.14.2'
go-version: '1.14.12'
- name: Install K3S
env:
INSTALL_K3S_VERSION: v0.5.0
INSTALL_K3S_VERSION: ${{ matrix.k3s-version }}+k3s1
run: |
set -x
curl -sfL https://get.k3s.io | sh -
@@ -309,10 +371,12 @@ jobs:
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
- name: Add /usr/local/bin to PATH
run: echo "::add-path::/usr/local/bin"
- name: Add ~/go/bin to PATH
run: echo "::add-path::/home/runner/go/bin"
run: |
echo "/home/runner/go/bin" >> $GITHUB_PATH
- name: Add /usr/local/bin to PATH
run: |
echo "/usr/local/bin" >> $GITHUB_PATH
- name: Download Go dependencies
run: |
go mod download
@@ -326,9 +390,9 @@ jobs:
git config --global user.email "john.doe@example.com"
- name: Pull Docker image required for tests
run: |
docker pull quay.io/dexidp/dex:v2.22.0
docker pull quay.io/dexidp/dex:v2.25.0
docker pull argoproj/argo-cd-ci-builder:v1.0.0
docker pull redis:5.0.8-alpine
docker pull redis:5.0.10-alpine
- name: Create target directory for binaries in the build-process
run: |
mkdir -p dist
@@ -341,7 +405,7 @@ jobs:
# port 8080 which is not visible in netstat -tulpen, but still there
# with a HTTP listener. We have API server listening on port 8088
# instead.
make start-e2e-local &
make start-e2e-local 2>&1 | sed -r "s/[[:cntrl:]]\[[0-9]{1,3}m//g" > /tmp/e2e-server.log &
count=1
until curl -f http://127.0.0.1:8088/healthz; do
sleep 10;
@@ -355,3 +419,9 @@ jobs:
run: |
set -x
make test-e2e-local
- name: Upload e2e-server logs
uses: actions/upload-artifact@v2
with:
name: e2e-server-k8s${{ matrix.k3s-version }}.log
path: /tmp/e2e-server.log
if: ${{ failure() }}

View File

@@ -13,7 +13,7 @@ jobs:
steps:
- uses: actions/setup-go@v1
with:
go-version: '1.14.1'
go-version: '1.14.12'
- uses: actions/checkout@master
with:
path: src/github.com/argoproj/argo-cd
@@ -47,4 +47,4 @@ jobs:
git config --global user.name 'CI'
git diff --exit-code && echo 'Already deployed' || (git commit -am 'Upgrade argocd to ${{ steps.image.outputs.tag }}' && git 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
# 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

@@ -23,11 +23,9 @@ jobs:
DRY_RUN: false
# Whether a draft release should be created, instead of public one
DRAFT_RELEASE: false
# The name of the repository containing tap formulae
TAP_REPOSITORY: argoproj/homebrew-tap
# 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 (or homebrew-tap repo specifically)
# access to public repositories
UPDATE_HOMEBREW: false
# Name of the GitHub user for Git config
GIT_USERNAME: argo-bot
@@ -46,7 +44,7 @@ jobs:
# 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
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
@@ -78,10 +76,10 @@ jobs:
fi
# Make the variables available in follow-up steps
echo "::set-env name=TARGET_VERSION::${TARGET_VERSION}"
echo "::set-env name=TARGET_BRANCH::${TARGET_BRANCH}"
echo "::set-env name=RELEASE_TAG::${RELEASE_TAG}"
echo "::set-env name=PRE_RELEASE::${PRE_RELEASE}"
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: |
@@ -103,16 +101,16 @@ jobs:
# 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
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
if echo "$line" | egrep -q '^commit [0-9a-f]+'; then
break
fi
echo $line >> ${RELEASE_NOTES}
echo "$line" >> ${RELEASE_NOTES}
fi
done
@@ -136,12 +134,12 @@ jobs:
# We store path to temporary release notes file for later reading, we
# need it when creating release.
echo "::set-env name=RELEASE_NOTES::$RELEASE_NOTES"
echo "RELEASE_NOTES=${RELEASE_NOTES}" >> $GITHUB_ENV
- name: Setup Golang
uses: actions/setup-go@v1
with:
go-version: '1.14.2'
go-version: '1.14.12'
- name: Setup Git author information
run: |
@@ -258,32 +256,17 @@ jobs:
asset_content_type: application/octet-stream
if: ${{ env.DRY_RUN != 'true' }}
- name: Check out homebrew tap repository
uses: actions/checkout@v2
- name: Update homebrew formula
env:
HOMEBREW_TOKEN: ${{ secrets.RELEASE_HOMEBREW_TOKEN }}
uses: dawidd6/action-homebrew-bump-formula@v3
with:
repository: ${{ env.TAP_REPOSITORY }}
path: homebrew-tap
fetch-depth: 0
token: ${{ env.HOMEBREW_TOKEN }}
if: ${{ env.HOMEBREW_TOKEN != '' && env.UPDATE_HOMEBREW == 'true' && env.PRE_RELEASE != 'true' }}
- name: Update homebrew tap formula
env:
HOMEBREW_TOKEN: ${{ secrets.RELEASE_HOMEBREW_TOKEN }}
run: |
set -ue
cd homebrew-tap
./update.sh argocd ${TARGET_VERSION}
git commit -am "Update argocd to ${TARGET_VERSION}"
git push
cd ..
rm -rf homebrew-tap
token: ${{env.HOMEBREW_TOKEN}}
formula: argocd
if: ${{ env.HOMEBREW_TOKEN != '' && env.UPDATE_HOMEBREW == 'true' && env.PRE_RELEASE != 'true' }}
- name: Delete original request tag from repository
run: |
set -ue
git push --delete origin ${SOURCE_TAG}
if: ${{ always() }}
if: ${{ always() }}

8
.gitignore vendored
View File

@@ -12,3 +12,11 @@ coverage.out
test-results
.scannerwork
.scratch
node_modules/
# ignore built binaries
cmd/argocd/argocd
cmd/argocd-application-controller/argocd-application-controller
cmd/argocd-repo-server/argocd-repo-server
cmd/argocd-server/argocd-server
cmd/argocd-util/argocd-util

7
.readthedocs.yml Normal file
View File

@@ -0,0 +1,7 @@
version: 2
formats: all
mkdocs:
fail_on_warning: false
python:
install:
- requirements: docs/requirements.txt

View File

@@ -1,6 +1,136 @@
# Changelog
## v1.7.0 (Unreleased)
## v1.8.0 (Unreleased)
### Mono-Repository Improvements
Enhanced performance during manifest generation from mono-repository - the repository that represents the
desired state of the whole cluster and contains hundreds of applications. The improved argocd-repo-server
now able to concurrently generate manifests from the same repository and for the same commit SHA. This
might provide 10x performance improvement of manifests generation.
### Annotation Based Path Detection
The feature that allows specifying which source repository directories influence the application manifest generation
using the `argocd.argoproj.io/manifest-generate-paths` annotation. The annotation improves the Git webhook handler
behavior. The webhook avoids related applications reconciliation if no related files have been changed by the Git commit
and even allows to skip manifests generation for new commit by re-using generation manifests for the previous commit.
### Horizontal Controller Scaling
This release allows scaling the `argocd-application-controller` horizontally. This allows you to manage as many Kubernetes clusters
as needed using a single Argo CD instance.
## New Core Functionality Features
Besides performance improvements, Argo CD got a lot of usability enhancements and new features:
* Namespace and CRD creation [#4354](https://github.com/argoproj/argo-cd/issues/4354)
* Unknown fields of built-in K8S types [#1787](https://github.com/argoproj/argo-cd/issues/1787)
* Endpoints Diffing [#1816](https://github.com/argoproj/argo-cd/issues/1816)
* Better compatibility with Helm Hooks [#1816](https://github.com/argoproj/argo-cd/issues/1816)
* App-of-Apps Health Assessment [#3781](https://github.com/argoproj/argo-cd/issues/3781)
## Global Projects
This release makes it easy to manage an Argo CD that has hundreds of Projects. Instead of duplicating the same organization-wide rules in all projects
you can put such rules into one project and make this project “global” for all other projects. Rules defined in the global project are inherited by all
other projects and therefore dont have to be duplicated. The sample below demonstrates how you can create a global project and specify which project should
inherit global project rules using Kubernetes labels.
## User Interface Improvements
The Argo CD user interface is an important part of a project and we keep working hard on improving the user experience. Here is an incomplete list of implemented improvements:
* Improved Applications Filters [#4622](https://github.com/argoproj/argo-cd/issues/4622)
* Git tags and branches autocompletion [#4713](https://github.com/argoproj/argo-cd/issues/4713)
* Project Details Page [#4400](https://github.com/argoproj/argo-cd/issues/4400)
* New version information panel [#4376](https://github.com/argoproj/argo-cd/issues/4376)
* Progress Indicators [#4411](https://github.com/argoproj/argo-cd/issues/4411)
* External links annotations [#4380](https://github.com/argoproj/argo-cd/issues/4380) and more!
## Config Management Tools Enhancements
* OCI Based Repositories [#4018](https://github.com/argoproj/argo-cd/issues/4018)
* Configurable Helm Versions [#4111](https://github.com/argoproj/argo-cd/issues/4111)
## Bug fixes and under the hood changes
In addition to new features and enhancements, weve fixed more than 50 bugs and upgraded third-party components and libraries that Argo CD relies on.
## v1.7.9 (2020-11-17)
- fix: improve commit verification tolerance (#4825)
- fix: argocd diff --local should not print data of local secrets (#4850)
- fix(ui): stack overflow crash of resource tree view for large applications (#4685)
- chore: Update golang to v1.14.12 [backport to release-1.7] (#4834)
- chore: Update redis to 5.0.10 (#4767)
- chore: Replace deprecated GH actions directives for integration tests (#4589)
## v1.7.8 (2020-10-15)
- fix(logging.go): changing marshaler for JSON logging to use gogo (#4319)
- fix: login with apiKey capability (#4557)
- fix: api-server should not try creating default project it is exists already (#4517)
- fix: JS error on application list page if app has no namespace (#4499)
## v1.7.7 (2020-09-28)
- fix: Support transition from a git managed namespace to auto create (#4401)
- fix: reduce memory spikes during cluster cache refresh (#4298)
- fix: No error/warning condition if application destination namespace not monitored by Argo CD (#4329)
- fix: Fix local diff/sync of apps using cluster name (#4201)
## v1.7.6 (2020-09-18)
- fix: Added cluster authentication to AKS clusters (#4265)
- fix: swagger UI stuck loading (#4377)
- fix: prevent 'argocd app sync' hangs if sync is completed too quickly (#4373)
- fix: argocd app wait/sync might stuck (#4350)
- fix: failed syncs are not retried soon enough (#4353)
## v1.7.5 (2020-09-15)
- fix: app create with -f should not ignore other options (#4322)
- fix: limit concurrent list requests accross all clusters (#4328)
- fix: fix possible deadlock in /v1/api/stream/applications and /v1/api/application APIs (#4315)
- fix: WatchResourceTree does not enforce RBAC (#4311)
- fix: app refresh API should use app resource version (#4303)
- fix: use informer instead of k8s watch to ensure app is refreshed (#4290)
## v1.7.4 (2020-09-04)
- fix: automatically stop watch API requests when page is hidden (#4269)
- fix: upgrade gitops-engine dependency (issues #4242, #1881) (#4268)
- fix: application stream API should not return 'ADDED' events if resource version is provided (#4260)
- fix: return parsing error (#3942)
- fix: JS error when using cluster filter in the /application view (#4247)
- fix: improve applications list page client side performance (#4244)
## v1.7.3 (2020-09-01)
- fix: application details page crash when app is deleted (#4229)
- fix: api-server unnecessary normalize projects on every start (#4219)
- fix: load only project names in UI (#4217)
- fix: Re-create already initialized ARGOCD_GNUPGHOME on startup (#4214) (#4223)
- fix: Add openshift as a dex connector type which requires a redirectURI (#4222)
- fix: Replace status.observedAt with redis pub/sub channels for resource tree updates (#1340) (#4208)
- fix: cache inconsistency of child resources (#4053) (#4202)
- fix: do not include kube-api check in application liveness flow (#4163)
## v1.7.2 (2020-08-27)
- fix: Sync hangs with cert-manager on latest RC (#4105)
- fix: support for PKCE for cli login (#2932)
## v1.7.2 (2020-08-25)
- fix: Unable to create project JWT token on K8S v1.15 (#4165)
- fix: Argo CD does not exclude creationTimestamp from diffing (#4157)
## v1.7.0 (2020-08-24)
### GnuPG Signature Verification

View File

@@ -4,7 +4,7 @@ ARG BASE_IMAGE=debian:10-slim
# 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 golang:1.14.1 as builder
FROM golang:1.14.12 as builder
RUN echo 'deb http://deb.debian.org/debian buster-backports main' >> /etc/apt/sources.list
@@ -87,7 +87,7 @@ WORKDIR /home/argocd
####################################################################################################
# Argo CD UI stage
####################################################################################################
FROM node:11.15.0 as argocd-ui
FROM node:12.18.4 as argocd-ui
WORKDIR /src
ADD ["ui/package.json", "ui/yarn.lock", "./"]
@@ -103,7 +103,7 @@ RUN NODE_ENV='production' yarn build
####################################################################################################
# Argo CD Build stage which performs the actual build of Argo CD binaries
####################################################################################################
FROM golang:1.14.1 as argocd-build
FROM golang:1.14.12 as argocd-build
COPY --from=builder /usr/local/bin/packr /usr/local/bin/packr

View File

@@ -43,11 +43,21 @@ ARGOCD_TEST_E2E?=true
ARGOCD_LINT_GOGC?=20
# Depending on where we are (legacy or non-legacy pwd), we need to use
# different Docker volume mounts for our source tree
LEGACY_PATH=$(GOPATH)/src/github.com/argoproj/argo-cd
ifeq ("$(PWD)","$(LEGACY_PATH)")
DOCKER_SRC_MOUNT="$(DOCKER_SRCDIR):/go/src$(VOLUME_MOUNT)"
else
DOCKER_SRC_MOUNT="$(PWD):/go/src/github.com/argoproj/argo-cd$(VOLUME_MOUNT)"
endif
# 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
docker run --rm -it \
--name argocd-test-server \
-u $(shell id -u):$(shell id -g) \
-e USER_ID=$(shell id -u) \
-e HOME=/home/user \
-e GOPATH=/go \
@@ -55,7 +65,7 @@ define run-in-test-server
-e ARGOCD_IN_CI=$(ARGOCD_IN_CI) \
-e ARGOCD_E2E_TEST=$(ARGOCD_E2E_TEST) \
-e ARGOCD_E2E_YARN_HOST=$(ARGOCD_E2E_YARN_HOST) \
-v ${DOCKER_SRCDIR}:/go/src${VOLUME_MOUNT} \
-v ${DOCKER_SRC_MOUNT} \
-v ${GOPATH}/pkg/mod:/go/pkg/mod${VOLUME_MOUNT} \
-v ${GOCACHE}:/tmp/go-build-cache${VOLUME_MOUNT} \
-v ${HOME}/.kube:/home/user/.kube${VOLUME_MOUNT} \
@@ -71,13 +81,13 @@ endef
define run-in-test-client
docker run --rm -it \
--name argocd-test-client \
-u $(shell id -u) \
-u $(shell id -u):$(shell id -g) \
-e HOME=/home/user \
-e GOPATH=/go \
-e ARGOCD_E2E_K3S=$(ARGOCD_E2E_K3S) \
-e GOCACHE=/tmp/go-build-cache \
-e ARGOCD_LINT_GOGC=$(ARGOCD_LINT_GOGC) \
-v ${DOCKER_SRCDIR}:/go/src${VOLUME_MOUNT} \
-v ${DOCKER_SRC_MOUNT} \
-v ${GOPATH}/pkg/mod:/go/pkg/mod${VOLUME_MOUNT} \
-v ${GOCACHE}:/tmp/go-build-cache${VOLUME_MOUNT} \
-v ${HOME}/.kube:/home/user/.kube${VOLUME_MOUNT} \
@@ -89,7 +99,7 @@ endef
#
define exec-in-test-server
docker exec -it -u $(shell id -u) -e ARGOCD_E2E_K3S=$(ARGOCD_E2E_K3S) argocd-test-server $(1)
docker exec -it -u $(shell id -u):$(shell id -g) -e ARGOCD_E2E_K3S=$(ARGOCD_E2E_K3S) argocd-test-server $(1)
endef
PATH:=$(PATH):$(PWD)/hack
@@ -134,28 +144,43 @@ endif
.PHONY: all
all: cli image argocd-util
# We have some legacy requirements for being checked out within $GOPATH.
# The ensure-gopath target can be used as dependency to ensure we are running
# within these boundaries.
.PHONY: ensure-gopath
ensure-gopath:
ifneq ("$(PWD)","$(LEGACY_PATH)")
@echo "Due to legacy requirements for codegen, repository needs to be checked out within \$$GOPATH"
@echo "Location of this repo should be '$(LEGACY_PATH)' but is '$(PWD)'"
@exit 1
endif
.PHONY: gogen
gogen:
gogen: ensure-gopath
export GO111MODULE=off
go generate ./util/argo/...
.PHONY: protogen
protogen:
protogen: ensure-gopath
export GO111MODULE=off
./hack/generate-proto.sh
.PHONY: openapigen
openapigen:
openapigen: ensure-gopath
export GO111MODULE=off
./hack/update-openapi.sh
.PHONY: clientgen
clientgen:
clientgen: ensure-gopath
export GO111MODULE=off
./hack/update-codegen.sh
.PHONY: clidocsgen
clidocsgen: ensure-gopath
go run tools/cmd-docs/main.go
.PHONY: codegen-local
codegen-local: mod-vendor-local gogen protogen clientgen openapigen manifests-local
codegen-local: ensure-gopath mod-vendor-local gogen protogen clientgen openapigen clidocsgen manifests-local
rm -rf vendor/
.PHONY: codegen
@@ -170,7 +195,8 @@ cli: test-tools-image
cli-local: clean-debug
CGO_ENABLED=0 ${PACKR_CMD} build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd/argocd
.PHONY: cli-docker
.PHONY: cli-argocd
cli-argocd:
go build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd/argocd
.PHONY: release-cli
@@ -193,7 +219,7 @@ argocd-util: clean-debug
.PHONY: test-tools-image
test-tools-image:
docker build -t $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE) -f test/container/Dockerfile .
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)
.PHONY: manifests-local
@@ -308,7 +334,7 @@ build: test-tools-image
# Build all Go code (local version)
.PHONY: build-local
build-local:
build-local:
go build -v `go list ./... | grep -v 'resource_customizations\|test/e2e'`
# Run all unit tests
@@ -329,10 +355,24 @@ test-local:
./hack/test.sh -coverprofile=coverage.out "$(TEST_MODULE)"; \
fi
.PHONY: test-race
test-race: test-tools-image
mkdir -p $(GOCACHE)
$(call run-in-test-client,make TEST_MODULE=$(TEST_MODULE) test-race-local)
# Run all unit tests, with data race detection, skipping known failures (local version)
.PHONY: test-race-local
test-race-local:
if test "$(TEST_MODULE)" = ""; then \
./hack/test.sh -race -coverprofile=coverage.out `go list ./... | grep -v 'test/e2e'`; \
else \
./hack/test.sh -race -coverprofile=coverage.out "$(TEST_MODULE)"; \
fi
# Run the E2E test suite. E2E test servers (see start-e2e target) must be
# started before.
.PHONY: test-e2e
test-e2e:
test-e2e:
$(call exec-in-test-server,make test-e2e-local)
# Run the E2E test suite (local version)
@@ -359,7 +399,7 @@ start-e2e: test-tools-image
# Starts e2e server locally (or within a container)
.PHONY: start-e2e-local
start-e2e-local:
start-e2e-local:
kubectl create ns argocd-e2e || true
kubectl config set-context --current --namespace=argocd-e2e
kustomize build test/manifests/base | kubectl apply -f -
@@ -367,7 +407,6 @@ start-e2e-local:
if test -d /tmp/argo-e2e/app/config/gpg; then rm -rf /tmp/argo-e2e/app/config/gpg/*; fi
mkdir -p /tmp/argo-e2e/app/config/gpg/keys && chmod 0700 /tmp/argo-e2e/app/config/gpg/keys
mkdir -p /tmp/argo-e2e/app/config/gpg/source && chmod 0700 /tmp/argo-e2e/app/config/gpg/source
if test "$(USER_ID)" != ""; then chown -R "$(USER_ID)" /tmp/argo-e2e; fi
# set paths for locally managed ssh known hosts and tls certs data
ARGOCD_SSH_DATA_PATH=/tmp/argo-e2e/app/config/ssh \
ARGOCD_TLS_DATA_PATH=/tmp/argo-e2e/app/config/tls \
@@ -378,7 +417,7 @@ start-e2e-local:
ARGOCD_ZJWT_FEATURE_FLAG=always \
ARGOCD_IN_CI=$(ARGOCD_IN_CI) \
ARGOCD_E2E_TEST=true \
goreman -f $(ARGOCD_PROCFILE) start
goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START}
# Cleans VSCode debug.test files from sub-dirs to prevent them from being included in packr boxes
.PHONY: clean-debug

5
OWNERS
View File

@@ -10,3 +10,8 @@ approvers:
- jessesuen
- mayzhang2000
- rachelwang20
reviewers:
- jgwest
- wtam2018
- tetchel

View File

@@ -1,7 +1,7 @@
controller: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} go run ./cmd/argocd-application-controller/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
api-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} go run ./cmd/argocd-server/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --staticassets ui/dist/app"
dex: sh -c "go run github.com/argoproj/argo-cd/cmd/argocd-util 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 quay.io/dexidp/dex:v2.22.0 serve /dex.yaml"
redis: docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} redis:5.0.8-alpine --save "" --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}
dex: sh -c "go run github.com/argoproj/argo-cd/cmd/argocd-util gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:v2.27.0 serve /dex.yaml"
redis: docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} redis:5.0.10-alpine --save "" --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}
repo-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} 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} go run ./cmd/argocd-repo-server/main.go --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379}"
ui: sh -c 'cd ui && ${ARGOCD_E2E_YARN_CMD:-yarn} start'
git-server: test/fixture/testrepos/start-git.sh

View File

@@ -27,6 +27,8 @@ Check live demo at https://cd.apps.argoproj.io/.
## Community Blogs and Presentations
1. [Environments Based On Pull Requests (PRs): Using Argo CD To Apply GitOps Principles On Previews](https://youtu.be/cpAaI8p4R60)
1. [Argo CD: Applying GitOps Principles To Manage Production Environment In Kubernetes](https://youtu.be/vpWQeoaiRM4)
1. [Tutorial: Everything You Need To Become A GitOps Ninja](https://www.youtube.com/watch?v=r50tRQjisxw) 90m tutorial on GitOps and Argo CD.
1. [Comparison of Argo CD, Spinnaker, Jenkins X, and Tekton](https://www.inovex.de/blog/spinnaker-vs-argo-cd-vs-tekton-vs-jenkins-x/)
1. [Simplify and Automate Deployments Using GitOps with IBM Multicloud Manager 3.1.2](https://medium.com/ibm-cloud/simplify-and-automate-deployments-using-gitops-with-ibm-multicloud-manager-3-1-2-4395af317359)
@@ -38,3 +40,5 @@ Check live demo at https://cd.apps.argoproj.io/.
1. [Argo CD - GitOps Continuous Delivery for Kubernetes](https://www.youtube.com/watch?v=aWDIQMbp1cc&feature=youtu.be&t=1m4s)
1. [Introduction to Argo CD : Kubernetes DevOps CI/CD](https://www.youtube.com/watch?v=2WSJF7d8dUg&feature=youtu.be)
1. [GitOps Deployment and Kubernetes - using ArgoCD](https://medium.com/riskified-technology/gitops-deployment-and-kubernetes-f1ab289efa4b)
1. [Deploy Argo CD with Ingress and TLS in Three Steps: No YAML Yak Shaving Required](https://itnext.io/deploy-argo-cd-with-ingress-and-tls-in-three-steps-no-yaml-yak-shaving-required-bc536d401491)
1. [GitOps Continuous Delivery with Argo and Codefresh](https://codefresh.io/events/cncf-member-webinar-gitops-continuous-delivery-argo-codefresh/)

47
SECURITY.md Normal file
View File

@@ -0,0 +1,47 @@
# Security Policy for Argo CD
Version: **v1.0 (2020-02-26)**
## Preface
As a deployment tool, Argo CD needs to have production access which makes
security a very important topic. The Argoproj team takes security very
seriously and is continuously working on improving it.
## Supported Versions
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 a best
effort basis, however, it cannot be guaranteed that security fixes get
back-ported to these unsupported versions.
In rare cases, where a security fix needs complex re-design of a feature or is
otherwise very intrusive, and there's a workaround available, we may decide to
provide a forward-fix only, e.g. to be released the next minor release, instead
of releasing it within a patch branch for the currently supported releases.
## Reporting a Vulnerability
If you find a security related bug in ArgoCD, we kindly ask you for responsible
disclosure and for giving us appropriate time to react, analyze and develop a
fix to mitigate the found security vulnerability.
We will do our best to react quickly on your inquiry, and to coordinate a fix
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 advisiories using the Git Hub SA 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 all of the following people:
* jfischer@redhat.com
* Jesse_Suen@intuit.com
* Alexander_Matyushentsev@intuit.com
* Edward_Lee@intuit.com

View File

@@ -5,12 +5,18 @@ As the Argo Community grows, we'd like to keep track of our users. Please send a
Currently, the following organizations are **officially** using Argo CD:
1. [127Labs](https://127labs.com/)
1. [3Rein](https://www.3rein.com/)
1. [Adevinta](https://www.adevinta.com/)
1. [AppDirect](https://www.appdirect.com)
1. [ANSTO - Australian Synchrotron](https://www.synchrotron.org.au/)
1. [ARZ Allgemeines Rechenzentrum GmbH ](https://www.arz.at/)
1. [Arctiq Inc.](https://www.arctiq.ca)
1. [Baloise](https://www.baloise.com)
1. [BCDevExchange DevOps Platform](https://bcdevexchange.org/DevOpsPlatform)
1. [Beat](https://thebeat.co/en/)
1. [Beez Innovation Labs](https://www.beezlabs.com/)
1. [BioBox Analytics](https://biobox.io)
1. [Camptocamp](https://camptocamp.com)
1. [CARFAX](https://www.carfax.com)
1. [Celonis](https://www.celonis.com/)
1. [Codility](https://www.codility.com/)
@@ -19,6 +25,7 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Cybozu](https://cybozu-global.com)
1. [D2iQ](https://www.d2iq.com)
1. [EDF Renewables](https://www.edf-re.com/)
1. [edX](https://edx.org)
1. [Electronic Arts Inc. ](https://www.ea.com)
1. [Elium](https://www.elium.com)
1. [END.](https://www.endclothing.com/)
@@ -32,6 +39,7 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Honestbank](https://honestbank.com)
1. [InsideBoard](https://www.insideboard.com)
1. [Intuit](https://www.intuit.com/)
1. [Kasa](https://kasa.co.kr/)
1. [KintoHub](https://www.kintohub.com/)
1. [KompiTech GmbH](https://www.kompitech.com/)
1. [LINE](https://linecorp.com/en/)
@@ -42,13 +50,17 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Mirantis](https://mirantis.com/)
1. [Money Forward](https://corp.moneyforward.com/en/)
1. [MOO Print](https://www.moo.com/)
1. [Nikkei](https://www.nikkei.co.jp/nikkeiinfo/en/)
1. [OpenSaaS Studio](https://opensaas.studio)
1. [Opensurvey](https://www.opensurvey.co.kr/)
1. [Optoro](https://www.optoro.com/)
1. [Peloton Interactive](https://www.onepeloton.com/)
1. [Pipefy](https://www.pipefy.com/)
1. [Preferred Networks](https://preferred.jp/en/)
1. [Prudential](https://prudential.com.sg)
1. [PUBG](https://www.pubg.com)
1. [QuintoAndar](https://quintoandar.com.br)
1. [Quipper](https://www.quipper.com/)
1. [Red Hat](https://www.redhat.com/)
1. [Robotinfra](https://www.robotinfra.com)
1. [Riskified](https://www.riskified.com/)
@@ -57,13 +69,17 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Swisscom](https://www.swisscom.ch)
1. [Swissquote](https://github.com/swissquote)
1. [Syncier](https://syncier.com/)
1. [TableCheck](https://tablecheck.com/)
1. [Tesla](https://tesla.com/)
1. [ThousandEyes](https://www.thousandeyes.com/)
1. [Ticketmaster](https://ticketmaster.com)
1. [Tiger Analytics](https://www.tigeranalytics.com/)
1. [Toss](https://toss.im/en)
1. [tru.ID](https://tru.id)
1. [Twilio SendGrid](https://sendgrid.com)
1. [tZERO](https://www.tzero.com/)
1. [UBIO](https://ub.io/)
1. [UFirstGroup](https://www.ufirstgroup.com/en/)
1. [Universidad Mesoamericana](https://www.umes.edu.gt/)
1. [Viaduct](https://www.viaduct.ai/)
1. [Volvo Cars](https://www.volvocars.com/)
@@ -75,3 +91,16 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Moengage](https://www.moengage.com/)
1. [LexisNexis](https://www.lexisnexis.com/)
1. [PayPay](https://paypay.ne.jp/)
1. [New Relic](https://newrelic.com/)
1. [Sumo Logic](https://sumologic.com/)
1. [Kinguin](https://www.kinguin.net/)
1. [Speee](https://speee.jp/)
1. [VISITS Technologies](https://visits.world/en)
1. [Qonto](https://qonto.com)
1. [openEuler](https://openeuler.org)
1. [MindSpore](https://mindspore.cn)
1. [openLooKeng](https://openlookeng.io)
1. [openGauss](https://opengauss.org/)
1. [Virtuo](https://www.govirtuo.com/)
1. [WeMo Scooter](https://www.wemoscooter.com/)
1. [Codefresh](https://www.codefresh.io/)

View File

@@ -1 +1 @@
1.7.0
1.8.7

View File

@@ -5,10 +5,6 @@
"produces": [
"application/json"
],
"schemes": [
"http",
"https"
],
"swagger": "2.0",
"info": {
"description": "Description of all APIs",
@@ -21,6 +17,7 @@
"tags": [
"AccountService"
],
"summary": "ListAccounts returns the list of accounts",
"operationId": "ListAccounts",
"responses": {
"200": {
@@ -37,6 +34,7 @@
"tags": [
"AccountService"
],
"summary": "CanI checks if the current account has permission to perform an action",
"operationId": "CanI",
"parameters": [
{
@@ -100,6 +98,7 @@
"tags": [
"AccountService"
],
"summary": "GetAccount returns an account",
"operationId": "GetAccount",
"parameters": [
{
@@ -124,7 +123,8 @@
"tags": [
"AccountService"
],
"operationId": "CreateTokenMixin10",
"summary": "CreateToken creates a token",
"operationId": "CreateToken",
"parameters": [
{
"type": "string",
@@ -156,7 +156,8 @@
"tags": [
"AccountService"
],
"operationId": "DeleteTokenMixin10",
"summary": "DeleteToken deletes a token",
"operationId": "DeleteToken",
"parameters": [
{
"type": "string",
@@ -187,7 +188,7 @@
"ApplicationService"
],
"summary": "List returns list of applications",
"operationId": "ListMixin9",
"operationId": "List",
"parameters": [
{
"type": "string",
@@ -238,7 +239,7 @@
"ApplicationService"
],
"summary": "Create creates an application",
"operationId": "CreateMixin9",
"operationId": "Create",
"parameters": [
{
"name": "body",
@@ -265,7 +266,7 @@
"ApplicationService"
],
"summary": "Update updates an application",
"operationId": "UpdateMixin9",
"operationId": "Update",
"parameters": [
{
"type": "string",
@@ -298,6 +299,7 @@
"tags": [
"ApplicationService"
],
"summary": "ManagedResources returns list of managed resources",
"operationId": "ManagedResources",
"parameters": [
{
@@ -347,6 +349,7 @@
"tags": [
"ApplicationService"
],
"summary": "ResourceTree returns resource tree",
"operationId": "ResourceTree",
"parameters": [
{
@@ -397,7 +400,7 @@
"ApplicationService"
],
"summary": "Get returns an application by name",
"operationId": "GetMixin9",
"operationId": "Get",
"parameters": [
{
"type": "string",
@@ -449,7 +452,7 @@
"ApplicationService"
],
"summary": "Delete deletes an application",
"operationId": "DeleteMixin9",
"operationId": "Delete",
"parameters": [
{
"type": "string",
@@ -667,7 +670,16 @@
"200": {
"description": "A successful response.(streaming responses)",
"schema": {
"$ref": "#/x-stream-definitions/applicationLogEntry"
"type": "object",
"title": "Stream result of applicationLogEntry",
"properties": {
"error": {
"$ref": "#/definitions/runtimeStreamError"
},
"result": {
"$ref": "#/definitions/applicationLogEntry"
}
}
}
}
}
@@ -813,6 +825,7 @@
"tags": [
"ApplicationService"
],
"summary": "ListResourceActions returns list of resource actions",
"operationId": "ListResourceActions",
"parameters": [
{
@@ -860,6 +873,7 @@
"tags": [
"ApplicationService"
],
"summary": "RunResourceAction run resource action",
"operationId": "RunResourceAction",
"parameters": [
{
@@ -1024,7 +1038,7 @@
"tags": [
"ApplicationService"
],
"summary": "Get returns an application by name",
"summary": "Get returns sync windows of the application",
"operationId": "GetApplicationSyncWindows",
"parameters": [
{
@@ -1148,7 +1162,7 @@
"ClusterService"
],
"summary": "List returns list of clusters",
"operationId": "List",
"operationId": "ListMixin3",
"parameters": [
{
"type": "string",
@@ -1175,7 +1189,7 @@
"ClusterService"
],
"summary": "Create creates a cluster",
"operationId": "Create",
"operationId": "CreateMixin3",
"parameters": [
{
"name": "body",
@@ -1202,7 +1216,7 @@
"ClusterService"
],
"summary": "Update updates a cluster",
"operationId": "Update",
"operationId": "UpdateMixin3",
"parameters": [
{
"type": "string",
@@ -1236,7 +1250,7 @@
"ClusterService"
],
"summary": "Get returns a cluster by server address",
"operationId": "GetMixin2",
"operationId": "GetMixin3",
"parameters": [
{
"type": "string",
@@ -1264,7 +1278,7 @@
"ClusterService"
],
"summary": "Delete deletes a cluster",
"operationId": "Delete",
"operationId": "DeleteMixin3",
"parameters": [
{
"type": "string",
@@ -1344,7 +1358,7 @@
"GPGKeyService"
],
"summary": "List all available repository certificates",
"operationId": "ListMixin7",
"operationId": "ListMixin4",
"parameters": [
{
"type": "string",
@@ -1367,7 +1381,7 @@
"GPGKeyService"
],
"summary": "Create one or more GPG public keys in the server's configuration",
"operationId": "CreateMixin7",
"operationId": "CreateMixin4",
"parameters": [
{
"description": "Raw key data of the GPG key(s) to create",
@@ -1393,7 +1407,7 @@
"GPGKeyService"
],
"summary": "Delete specified GPG public key from the server's configuration",
"operationId": "DeleteMixin7",
"operationId": "DeleteMixin4",
"parameters": [
{
"type": "string",
@@ -1418,7 +1432,7 @@
"GPGKeyService"
],
"summary": "Get information about specified GPG public key from the server",
"operationId": "GetMixin7",
"operationId": "GetMixin4",
"parameters": [
{
"type": "string",
@@ -1444,7 +1458,7 @@
"ProjectService"
],
"summary": "List returns list of projects",
"operationId": "ListMixin6",
"operationId": "ListMixin5",
"parameters": [
{
"type": "string",
@@ -1465,8 +1479,8 @@
"tags": [
"ProjectService"
],
"summary": "Create a new project.",
"operationId": "CreateMixin6",
"summary": "Create a new project",
"operationId": "CreateMixin5",
"parameters": [
{
"name": "body",
@@ -1493,7 +1507,7 @@
"ProjectService"
],
"summary": "Get returns a project by name",
"operationId": "GetMixin6",
"operationId": "GetMixin5",
"parameters": [
{
"type": "string",
@@ -1516,7 +1530,7 @@
"ProjectService"
],
"summary": "Delete deletes a project",
"operationId": "DeleteMixin6",
"operationId": "DeleteMixin5",
"parameters": [
{
"type": "string",
@@ -1560,6 +1574,31 @@
}
}
},
"/api/v1/projects/{name}/globalprojects": {
"get": {
"tags": [
"ProjectService"
],
"summary": "Get returns a virtual project by name",
"operationId": "GetGlobalProjects",
"parameters": [
{
"type": "string",
"name": "name",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/projectGlobalProjectsResponse"
}
}
}
}
},
"/api/v1/projects/{name}/syncwindows": {
"get": {
"tags": [
@@ -1591,7 +1630,7 @@
"ProjectService"
],
"summary": "Update updates a project",
"operationId": "UpdateMixin6",
"operationId": "UpdateMixin5",
"parameters": [
{
"type": "string",
@@ -1624,8 +1663,8 @@
"tags": [
"ProjectService"
],
"summary": "Create a new project token.",
"operationId": "CreateToken",
"summary": "Create a new project token",
"operationId": "CreateTokenMixin5",
"parameters": [
{
"type": "string",
@@ -1663,8 +1702,8 @@
"tags": [
"ProjectService"
],
"summary": "Delete a new project token.",
"operationId": "DeleteToken",
"summary": "Delete a new project token",
"operationId": "DeleteTokenMixin5",
"parameters": [
{
"type": "string",
@@ -1909,7 +1948,7 @@
"RepositoryService"
],
"summary": "Get returns a repository or its credentials",
"operationId": "GetMixin3",
"operationId": "GetMixin7",
"parameters": [
{
"type": "string",
@@ -1972,7 +2011,7 @@
"tags": [
"RepositoryService"
],
"summary": "ListApps returns list of apps in the repo",
"summary": "ListApps returns list of apps in the repe",
"operationId": "ListApps",
"parameters": [
{
@@ -2002,6 +2041,7 @@
"tags": [
"RepositoryService"
],
"summary": "GetHelmCharts returns list of helm charts in the specified repository",
"operationId": "GetHelmCharts",
"parameters": [
{
@@ -2029,6 +2069,38 @@
}
}
},
"/api/v1/repositories/{repo}/refs": {
"get": {
"tags": [
"RepositoryService"
],
"operationId": "ListRefs",
"parameters": [
{
"type": "string",
"description": "Repo URL for query",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "boolean",
"format": "boolean",
"description": "Whether to force a cache refresh on repo's connection state.",
"name": "forceRefresh",
"in": "query"
}
],
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/repositoryRefs"
}
}
}
}
},
"/api/v1/repositories/{repo}/validate": {
"post": {
"tags": [
@@ -2103,8 +2175,8 @@
"tags": [
"SessionService"
],
"summary": "Create a new JWT for authentication and set a cookie if using HTTP.",
"operationId": "CreateMixin11",
"summary": "Create a new JWT for authentication and set a cookie if using HTTP",
"operationId": "CreateMixin8",
"parameters": [
{
"name": "body",
@@ -2128,8 +2200,8 @@
"tags": [
"SessionService"
],
"summary": "Delete an existing JWT cookie if using HTTP.",
"operationId": "DeleteMixin11",
"summary": "Delete an existing JWT cookie if using HTTP",
"operationId": "DeleteMixin8",
"responses": {
"200": {
"description": "A successful response.",
@@ -2163,7 +2235,7 @@
"SettingsService"
],
"summary": "Get returns Argo CD settings",
"operationId": "Get",
"operationId": "GetMixin10",
"responses": {
"200": {
"description": "A successful response.",
@@ -2179,7 +2251,7 @@
"tags": [
"ApplicationService"
],
"summary": "Watch returns stream of application change events.",
"summary": "Watch returns stream of application change events",
"operationId": "Watch",
"parameters": [
{
@@ -2221,7 +2293,75 @@
"200": {
"description": "A successful response.(streaming responses)",
"schema": {
"$ref": "#/x-stream-definitions/v1alpha1ApplicationWatchEvent"
"type": "object",
"title": "Stream result of v1alpha1ApplicationWatchEvent",
"properties": {
"error": {
"$ref": "#/definitions/runtimeStreamError"
},
"result": {
"$ref": "#/definitions/v1alpha1ApplicationWatchEvent"
}
}
}
}
}
}
},
"/api/v1/stream/applications/{applicationName}/resource-tree": {
"get": {
"tags": [
"ApplicationService"
],
"summary": "Watch returns stream of application resource tree",
"operationId": "WatchResourceTree",
"parameters": [
{
"type": "string",
"name": "applicationName",
"in": "path",
"required": true
},
{
"type": "string",
"name": "namespace",
"in": "query"
},
{
"type": "string",
"name": "name",
"in": "query"
},
{
"type": "string",
"name": "version",
"in": "query"
},
{
"type": "string",
"name": "group",
"in": "query"
},
{
"type": "string",
"name": "kind",
"in": "query"
}
],
"responses": {
"200": {
"description": "A successful response.(streaming responses)",
"schema": {
"type": "object",
"title": "Stream result of v1alpha1ApplicationTree",
"properties": {
"error": {
"$ref": "#/definitions/runtimeStreamError"
},
"result": {
"$ref": "#/definitions/v1alpha1ApplicationTree"
}
}
}
}
}
@@ -2484,6 +2624,10 @@
"content": {
"type": "string"
},
"last": {
"type": "boolean",
"format": "boolean"
},
"timeStamp": {
"$ref": "#/definitions/v1Time"
}
@@ -2514,6 +2658,19 @@
}
}
},
"applicationv1alpha1EnvEntry": {
"type": "object",
"properties": {
"name": {
"type": "string",
"title": "the name, usually uppercase"
},
"value": {
"type": "string",
"title": "the value"
}
}
},
"clusterClusterResponse": {
"type": "object"
},
@@ -2706,6 +2863,17 @@
"projectEmptyResponse": {
"type": "object"
},
"projectGlobalProjectsResponse": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/v1alpha1AppProject"
}
}
}
},
"projectProjectCreateRequest": {
"description": "ProjectCreateRequest defines project creation parameters.",
"type": "object",
@@ -2951,6 +3119,24 @@
}
}
},
"repositoryRefs": {
"type": "object",
"title": "A subset of the repository's named refs",
"properties": {
"branches": {
"type": "array",
"items": {
"type": "string"
}
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"repositoryRepoAppDetailsQuery": {
"type": "object",
"title": "RepoAppDetailsQuery contains query information for app details request",
@@ -3153,10 +3339,6 @@
},
"lastObservedTime": {
"$ref": "#/definitions/v1MicroTime"
},
"state": {
"type": "string",
"title": "State of this Series: Ongoing or Finished\nDeprecated. Planned removal for 1.18"
}
}
},
@@ -3175,7 +3357,7 @@
}
},
"v1FieldsV1": {
"description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set,\nor a string representing a sub-field or item. The string will follow one of these four formats:\n'f:<name>', where <name> is the name of a field in a struct, or key in a map\n'v:<value>', where <value> is the exact json formatted value of a list item\n'i:<index>', where <index> is position of a item in a list\n'k:<keys>', where <keys> is a map of a list item's key fields to their unique values\nIf a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff",
"description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set,\nor a string representing a sub-field or item. The string will follow one of these four formats:\n'f:<name>', where <name> is the name of a field in a struct, or key in a map\n'v:<value>', where <value> is the exact json formatted value of a list item\n'i:<index>', where <index> is position of a item in a list\n'k:<keys>', where <keys> is a map of a list item's key fields to their unique values\nIf a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff\n+protobuf.options.(gogoproto.goproto_stringer)=false",
"type": "object",
"properties": {
"Raw": {
@@ -3340,7 +3522,7 @@
"title": "Name must be unique within a namespace. Is required when creating resources, although\nsome resources may allow a client to request the generation of an appropriate name\nautomatically. Name is primarily intended for creation idempotence and configuration\ndefinition.\nCannot be updated.\nMore info: http://kubernetes.io/docs/user-guide/identifiers#names\n+optional"
},
"namespace": {
"description": "Namespace defines the space within each name must be unique. An empty namespace is\nequivalent to the \"default\" namespace, but \"default\" is the canonical representation.\nNot all objects are required to be scoped to a namespace - the value of this field for\nthose objects will be empty.\n\nMust be a DNS_LABEL.\nCannot be updated.\nMore info: http://kubernetes.io/docs/user-guide/namespaces\n+optional",
"description": "Namespace defines the space within which each name must be unique. An empty namespace is\nequivalent to the \"default\" namespace, but \"default\" is the canonical representation.\nNot all objects are required to be scoped to a namespace - the value of this field for\nthose objects will be empty.\n\nMust be a DNS_LABEL.\nCannot be updated.\nMore info: http://kubernetes.io/docs/user-guide/namespaces\n+optional",
"type": "string"
},
"ownerReferences": {
@@ -3580,7 +3762,7 @@
},
"v1alpha1Application": {
"type": "object",
"title": "Application is a definition of Application resource.\n+genclient\n+genclient:noStatus\n+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n+kubebuilder:resource:path=applications,shortName=app;apps",
"title": "Application is a definition of Application resource.\n+genclient\n+genclient:noStatus\n+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n+kubebuilder:resource:path=applications,shortName=app;apps\n+kubebuilder:printcolumn:name=\"Sync Status\",type=string,JSONPath=`.status.sync.status`\n+kubebuilder:printcolumn:name=\"Health Status\",type=string,JSONPath=`.status.health.status`\n+kubebuilder:printcolumn:name=\"Revision\",type=string,JSONPath=`.status.sync.revision`,priority=10",
"properties": {
"metadata": {
"$ref": "#/definitions/v1ObjectMeta"
@@ -3686,6 +3868,9 @@
"v1alpha1ApplicationSourceDirectory": {
"type": "object",
"properties": {
"exclude": {
"type": "string"
},
"jsonnet": {
"$ref": "#/definitions/v1alpha1ApplicationSourceJsonnet"
},
@@ -3727,6 +3912,10 @@
"values": {
"type": "string",
"title": "Values is Helm values, typically defined as a block"
},
"version": {
"type": "string",
"title": "Version is the Helm version to use for templating with"
}
}
},
@@ -3778,6 +3967,13 @@
"type": "object",
"title": "ApplicationSourceKustomize holds kustomize specific options",
"properties": {
"commonAnnotations": {
"type": "object",
"title": "CommonAnnotations adds additional kustomize commonAnnotations",
"additionalProperties": {
"type": "string"
}
},
"commonLabels": {
"type": "object",
"title": "CommonLabels adds additional kustomize commonLabels",
@@ -3813,7 +4009,7 @@
"env": {
"type": "array",
"items": {
"$ref": "#/definitions/v1alpha1EnvEntry"
"$ref": "#/definitions/applicationv1alpha1EnvEntry"
}
},
"name": {
@@ -4008,6 +4204,11 @@
"serverVersion": {
"type": "string",
"title": "DEPRECATED: use Info.ServerVersion field instead.\nThe server version"
},
"shard": {
"description": "Shard contains optional shard number. Calculated on the fly by the application controller if not specified.",
"type": "string",
"format": "int64"
}
}
},
@@ -4040,6 +4241,9 @@
"description": "Server requires Bearer authentication. This client will not attempt to use\nrefresh tokens for an OAuth2 flow.\nTODO: demonstrate an OAuth2 compatible client.",
"type": "string"
},
"execProviderConfig": {
"$ref": "#/definitions/v1alpha1ExecProviderConfig"
},
"password": {
"type": "string"
},
@@ -4145,16 +4349,35 @@
}
}
},
"v1alpha1EnvEntry": {
"v1alpha1ExecProviderConfig": {
"type": "object",
"title": "ExecProviderConfig is config used to call an external command to perform cluster authentication\nSee: https://godoc.org/k8s.io/client-go/tools/clientcmd/api#ExecConfig",
"properties": {
"name": {
"apiVersion": {
"type": "string",
"title": "the name, usually uppercase"
"title": "Preferred input version of the ExecInfo"
},
"value": {
"args": {
"type": "array",
"title": "Arguments to pass to the command when executing it",
"items": {
"type": "string"
}
},
"command": {
"type": "string",
"title": "the value"
"title": "Command to execute"
},
"env": {
"type": "object",
"title": "Env defines additional environment variables to expose to the process",
"additionalProperties": {
"type": "string"
}
},
"installHint": {
"type": "string",
"title": "This text is shown to the user when the executable doesn't seem to be present"
}
}
},
@@ -4558,6 +4781,11 @@
"format": "boolean",
"title": "Whether git-lfs support should be enabled for this repo"
},
"enableOCI": {
"type": "boolean",
"format": "boolean",
"title": "Whether helm-oci support should be enabled for this repo"
},
"inheritedCreds": {
"type": "boolean",
"format": "boolean",
@@ -5146,6 +5374,11 @@
"type": "object",
"title": "SyncPolicyAutomated controls the behavior of an automated sync",
"properties": {
"allowEmpty": {
"type": "boolean",
"format": "boolean",
"title": "AllowEmpty allows apps have zero live resources (default: false)"
},
"prune": {
"type": "boolean",
"format": "boolean",
@@ -5304,6 +5537,9 @@
"HelmVersion": {
"type": "string"
},
"JsonnetVersion": {
"type": "string"
},
"KsonnetVersion": {
"type": "string"
},

View File

@@ -0,0 +1,149 @@
package commands
import (
"context"
"math"
"time"
"github.com/argoproj/pkg/stats"
"github.com/go-redis/redis/v8"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/controller"
"github.com/argoproj/argo-cd/controller/sharding"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
"github.com/argoproj/argo-cd/reposerver/apiclient"
cacheutil "github.com/argoproj/argo-cd/util/cache"
appstatecache "github.com/argoproj/argo-cd/util/cache/appstate"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/env"
"github.com/argoproj/argo-cd/util/errors"
kubeutil "github.com/argoproj/argo-cd/util/kube"
"github.com/argoproj/argo-cd/util/settings"
)
const (
// CLIName is the name of the CLI
cliName = "argocd-application-controller"
// Default time in seconds for application resync period
defaultAppResyncPeriod = 180
)
func NewCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
appResyncPeriod int64
repoServerAddress string
repoServerTimeoutSeconds int
selfHealTimeoutSeconds int
statusProcessors int
operationProcessors int
logFormat string
logLevel string
glogLevel int
metricsPort int
kubectlParallelismLimit int64
cacheSrc func() (*appstatecache.Cache, error)
redisClient *redis.Client
)
var command = cobra.Command{
Use: cliName,
Short: "Run ArgoCD Application Controller",
Long: "ArgoCD application controller is a Kubernetes controller that continuously monitors running applications and compares the current, live state against the desired target state (as specified in the repo). This command runs Application Controller in the foreground. It can be configured by following options.",
DisableAutoGenTag: true,
RunE: func(c *cobra.Command, args []string) error {
cli.SetLogFormat(logFormat)
cli.SetLogLevel(logLevel)
cli.SetGLogLevel(glogLevel)
config, err := clientConfig.ClientConfig()
errors.CheckError(err)
errors.CheckError(v1alpha1.SetK8SConfigDefaults(config))
kubeClient := kubernetes.NewForConfigOrDie(config)
appClient := appclientset.NewForConfigOrDie(config)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
resyncDuration := time.Duration(appResyncPeriod) * time.Second
repoClientset := apiclient.NewRepoServerClientset(repoServerAddress, repoServerTimeoutSeconds)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
cache, err := cacheSrc()
errors.CheckError(err)
cache.Cache.SetClient(cacheutil.NewTwoLevelClient(cache.Cache.GetClient(), 10*time.Minute))
settingsMgr := settings.NewSettingsManager(ctx, kubeClient, namespace)
kubectl := kubeutil.NewKubectl()
clusterFilter := getClusterFilter()
appController, err := controller.NewApplicationController(
namespace,
settingsMgr,
kubeClient,
appClient,
repoClientset,
cache,
kubectl,
resyncDuration,
time.Duration(selfHealTimeoutSeconds)*time.Second,
metricsPort,
kubectlParallelismLimit,
clusterFilter)
errors.CheckError(err)
cacheutil.CollectMetrics(redisClient, appController.GetMetricsServer())
vers := common.GetVersion()
log.Infof("Application Controller (version: %s, built: %s) starting (namespace: %s)", vers.Version, vers.BuildDate, namespace)
stats.RegisterStackDumper()
stats.StartStatsTicker(10 * time.Minute)
stats.RegisterHeapDumper("memprofile")
go appController.Run(ctx, statusProcessors, operationProcessors)
// Wait forever
select {}
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().Int64Var(&appResyncPeriod, "app-resync", defaultAppResyncPeriod, "Time period in seconds for application resync.")
command.Flags().StringVar(&repoServerAddress, "repo-server", common.DefaultRepoServerAddr, "Repo server address.")
command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", 60, "Repo server RPC call timeout seconds.")
command.Flags().IntVar(&statusProcessors, "status-processors", 1, "Number of application status processors")
command.Flags().IntVar(&operationProcessors, "operation-processors", 1, "Number of application operation processors")
command.Flags().StringVar(&logFormat, "logformat", "text", "Set the logging format. One of: text|json")
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
command.Flags().IntVar(&glogLevel, "gloglevel", 0, "Set the glog logging level")
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDMetrics, "Start metrics server on given port")
command.Flags().IntVar(&selfHealTimeoutSeconds, "self-heal-timeout-seconds", 5, "Specifies timeout between application self heal attempts")
command.Flags().Int64Var(&kubectlParallelismLimit, "kubectl-parallelism-limit", 20, "Number of allowed concurrent kubectl fork/execs. Any value less the 1 means no limit.")
cacheSrc = appstatecache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
redisClient = client
})
return &command
}
func getClusterFilter() func(cluster *v1alpha1.Cluster) bool {
replicas := env.ParseNumFromEnv(common.EnvControllerReplicas, 0, 0, math.MaxInt32)
shard := env.ParseNumFromEnv(common.EnvControllerShard, -1, -math.MaxInt32, math.MaxInt32)
var clusterFilter func(cluster *v1alpha1.Cluster) bool
if replicas > 1 {
if shard < 0 {
var err error
shard, err = sharding.InferShard()
errors.CheckError(err)
}
log.Infof("Processing clusters from shard %d", shard)
clusterFilter = sharding.GetClusterFilter(replicas, shard)
} else {
log.Info("Processing all cluster shards")
}
return clusterFilter
}

View File

@@ -1,136 +1,21 @@
package main
import (
"context"
"fmt"
"os"
"time"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/argoproj/pkg/stats"
"github.com/go-redis/redis"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
// load the gcp plugin (required to authenticate against GKE clusters).
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
// load the oidc plugin (required to authenticate with OpenID Connect).
_ "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/common"
"github.com/argoproj/argo-cd/controller"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
"github.com/argoproj/argo-cd/reposerver/apiclient"
cacheutil "github.com/argoproj/argo-cd/util/cache"
appstatecache "github.com/argoproj/argo-cd/util/cache/appstate"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/settings"
"github.com/argoproj/argo-cd/cmd/argocd-application-controller/commands"
)
const (
// CLIName is the name of the CLI
cliName = "argocd-application-controller"
// Default time in seconds for application resync period
defaultAppResyncPeriod = 180
)
func newCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
appResyncPeriod int64
repoServerAddress string
repoServerTimeoutSeconds int
selfHealTimeoutSeconds int
statusProcessors int
operationProcessors int
logFormat string
logLevel string
glogLevel int
metricsPort int
kubectlParallelismLimit int64
cacheSrc func() (*appstatecache.Cache, error)
redisClient *redis.Client
)
var command = cobra.Command{
Use: cliName,
Short: "application-controller is a controller to operate on applications CRD",
RunE: func(c *cobra.Command, args []string) error {
cli.SetLogFormat(logFormat)
cli.SetLogLevel(logLevel)
cli.SetGLogLevel(glogLevel)
config, err := clientConfig.ClientConfig()
errors.CheckError(err)
errors.CheckError(v1alpha1.SetK8SConfigDefaults(config))
kubeClient := kubernetes.NewForConfigOrDie(config)
appClient := appclientset.NewForConfigOrDie(config)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
resyncDuration := time.Duration(appResyncPeriod) * time.Second
repoClientset := apiclient.NewRepoServerClientset(repoServerAddress, repoServerTimeoutSeconds)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
cache, err := cacheSrc()
errors.CheckError(err)
settingsMgr := settings.NewSettingsManager(ctx, kubeClient, namespace)
kubectl := &kube.KubectlCmd{}
appController, err := controller.NewApplicationController(
namespace,
settingsMgr,
kubeClient,
appClient,
repoClientset,
cache,
kubectl,
resyncDuration,
time.Duration(selfHealTimeoutSeconds)*time.Second,
metricsPort,
kubectlParallelismLimit)
errors.CheckError(err)
cacheutil.CollectMetrics(redisClient, appController.GetMetricsServer())
vers := common.GetVersion()
log.Infof("Application Controller (version: %s, built: %s) starting (namespace: %s)", vers.Version, vers.BuildDate, namespace)
stats.RegisterStackDumper()
stats.StartStatsTicker(10 * time.Minute)
stats.RegisterHeapDumper("memprofile")
go appController.Run(ctx, statusProcessors, operationProcessors)
// Wait forever
select {}
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().Int64Var(&appResyncPeriod, "app-resync", defaultAppResyncPeriod, "Time period in seconds for application resync.")
command.Flags().StringVar(&repoServerAddress, "repo-server", common.DefaultRepoServerAddr, "Repo server address.")
command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", 60, "Repo server RPC call timeout seconds.")
command.Flags().IntVar(&statusProcessors, "status-processors", 1, "Number of application status processors")
command.Flags().IntVar(&operationProcessors, "operation-processors", 1, "Number of application operation processors")
command.Flags().StringVar(&logFormat, "logformat", "text", "Set the logging format. One of: text|json")
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
command.Flags().IntVar(&glogLevel, "gloglevel", 0, "Set the glog logging level")
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDMetrics, "Start metrics server on given port")
command.Flags().IntVar(&selfHealTimeoutSeconds, "self-heal-timeout-seconds", 5, "Specifies timeout between application self heal attempts")
command.Flags().Int64Var(&kubectlParallelismLimit, "kubectl-parallelism-limit", 20, "Number of allowed concurrent kubectl fork/execs. Any value less the 1 means no limit.")
cacheSrc = appstatecache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
redisClient = client
})
return &command
}
func main() {
if err := newCommand().Execute(); err != nil {
if err := commands.NewCommand().Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}

View File

@@ -0,0 +1,162 @@
package commands
import (
"fmt"
"math"
"net"
"net/http"
"os"
"time"
"github.com/argoproj/pkg/stats"
"github.com/go-redis/redis/v8"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"google.golang.org/grpc/health/grpc_health_v1"
"github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/reposerver"
"github.com/argoproj/argo-cd/reposerver/apiclient"
reposervercache "github.com/argoproj/argo-cd/reposerver/cache"
"github.com/argoproj/argo-cd/reposerver/metrics"
"github.com/argoproj/argo-cd/reposerver/repository"
cacheutil "github.com/argoproj/argo-cd/util/cache"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/env"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/gpg"
"github.com/argoproj/argo-cd/util/healthz"
ioutil "github.com/argoproj/argo-cd/util/io"
"github.com/argoproj/argo-cd/util/tls"
)
const (
// CLIName is the name of the CLI
cliName = "argocd-repo-server"
gnuPGSourcePath = "/app/config/gpg/source"
defaultPauseGenerationAfterFailedGenerationAttempts = 3
defaultPauseGenerationOnFailureForMinutes = 60
defaultPauseGenerationOnFailureForRequests = 0
)
func getGnuPGSourcePath() string {
if path := os.Getenv("ARGOCD_GPG_DATA_PATH"); path != "" {
return path
} else {
return gnuPGSourcePath
}
}
func getPauseGenerationAfterFailedGenerationAttempts() int {
return env.ParseNumFromEnv(common.EnvPauseGenerationAfterFailedAttempts, defaultPauseGenerationAfterFailedGenerationAttempts, 0, math.MaxInt32)
}
func getPauseGenerationOnFailureForMinutes() int {
return env.ParseNumFromEnv(common.EnvPauseGenerationMinutes, defaultPauseGenerationOnFailureForMinutes, 0, math.MaxInt32)
}
func getPauseGenerationOnFailureForRequests() int {
return env.ParseNumFromEnv(common.EnvPauseGenerationRequests, defaultPauseGenerationOnFailureForRequests, 0, math.MaxInt32)
}
func NewCommand() *cobra.Command {
var (
logFormat string
logLevel string
parallelismLimit int64
listenPort int
metricsPort int
cacheSrc func() (*reposervercache.Cache, error)
tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error)
redisClient *redis.Client
)
var command = cobra.Command{
Use: cliName,
Short: "Run ArgoCD Repository Server",
Long: "ArgoCD Repository Server is an internal service which maintains a local cache of the Git repository holding the application manifests, and is responsible for generating and returning the Kubernetes manifests. This command runs Repository Server in the foreground. It can be configured by following options.",
DisableAutoGenTag: true,
RunE: func(c *cobra.Command, args []string) error {
cli.SetLogFormat(logFormat)
cli.SetLogLevel(logLevel)
tlsConfigCustomizer, err := tlsConfigCustomizerSrc()
errors.CheckError(err)
cache, err := cacheSrc()
errors.CheckError(err)
metricsServer := metrics.NewMetricsServer()
cacheutil.CollectMetrics(redisClient, metricsServer)
server, err := reposerver.NewServer(metricsServer, cache, tlsConfigCustomizer, repository.RepoServerInitConstants{
ParallelismLimit: parallelismLimit,
PauseGenerationAfterFailedGenerationAttempts: getPauseGenerationAfterFailedGenerationAttempts(),
PauseGenerationOnFailureForMinutes: getPauseGenerationOnFailureForMinutes(),
PauseGenerationOnFailureForRequests: getPauseGenerationOnFailureForRequests(),
})
errors.CheckError(err)
grpc := server.CreateGRPC()
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", listenPort))
errors.CheckError(err)
healthz.ServeHealthCheck(http.DefaultServeMux, func(r *http.Request) error {
if val, ok := r.URL.Query()["full"]; ok && len(val) > 0 && val[0] == "true" {
// connect to itself to make sure repo server is able to serve connection
// used by liveness probe to auto restart repo server
// see https://github.com/argoproj/argo-cd/issues/5110 for more information
conn, err := apiclient.NewConnection(fmt.Sprintf("localhost:%d", listenPort), 60)
if err != nil {
return err
}
defer ioutil.Close(conn)
client := grpc_health_v1.NewHealthClient(conn)
res, err := client.Check(r.Context(), &grpc_health_v1.HealthCheckRequest{})
if err != nil {
return err
}
if res.Status != grpc_health_v1.HealthCheckResponse_SERVING {
return fmt.Errorf("grpc health check status is '%v'", res.Status)
}
return nil
}
return nil
})
http.Handle("/metrics", metricsServer.GetHandler())
go func() { errors.CheckError(http.ListenAndServe(fmt.Sprintf(":%d", metricsPort), nil)) }()
if gpg.IsGPGEnabled() {
log.Infof("Initializing GnuPG keyring at %s", common.GetGnuPGHomePath())
err = gpg.InitializeGnuPG()
errors.CheckError(err)
log.Infof("Populating GnuPG keyring with keys from %s", getGnuPGSourcePath())
added, removed, err := gpg.SyncKeyRingFromDirectory(getGnuPGSourcePath())
errors.CheckError(err)
log.Infof("Loaded %d (and removed %d) keys from keyring", len(added), len(removed))
go func() { errors.CheckError(reposerver.StartGPGWatcher(getGnuPGSourcePath())) }()
}
log.Infof("argocd-repo-server %s serving on %s", common.GetVersion(), listener.Addr())
stats.RegisterStackDumper()
stats.StartStatsTicker(10 * time.Minute)
stats.RegisterHeapDumper("memprofile")
err = grpc.Serve(listener)
errors.CheckError(err)
return nil
},
}
command.Flags().StringVar(&logFormat, "logformat", "text", "Set the logging format. One of: text|json")
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
command.Flags().Int64Var(&parallelismLimit, "parallelismlimit", 0, "Limit on number of concurrent manifests generate requests. Any value less the 1 means no limit.")
command.Flags().IntVar(&listenPort, "port", common.DefaultPortRepoServer, "Listen on given port for incoming connections")
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortRepoServerMetrics, "Start metrics server on given port")
tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command)
cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
redisClient = client
})
return &command
}

View File

@@ -2,114 +2,13 @@ package main
import (
"fmt"
"net"
"net/http"
"os"
"time"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
"github.com/argoproj/pkg/stats"
"github.com/go-redis/redis"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/reposerver"
reposervercache "github.com/argoproj/argo-cd/reposerver/cache"
"github.com/argoproj/argo-cd/reposerver/metrics"
cacheutil "github.com/argoproj/argo-cd/util/cache"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/gpg"
"github.com/argoproj/argo-cd/util/tls"
"github.com/argoproj/argo-cd/cmd/argocd-repo-server/commands"
)
const (
// CLIName is the name of the CLI
cliName = "argocd-repo-server"
gnuPGSourcePath = "/app/config/gpg/source"
)
func getGnuPGSourcePath() string {
if path := os.Getenv("ARGOCD_GPG_DATA_PATH"); path != "" {
return path
} else {
return gnuPGSourcePath
}
}
func newCommand() *cobra.Command {
var (
logFormat string
logLevel string
parallelismLimit int64
listenPort int
metricsPort int
cacheSrc func() (*reposervercache.Cache, error)
tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error)
redisClient *redis.Client
)
var command = cobra.Command{
Use: cliName,
Short: "Run argocd-repo-server",
RunE: func(c *cobra.Command, args []string) error {
cli.SetLogFormat(logFormat)
cli.SetLogLevel(logLevel)
tlsConfigCustomizer, err := tlsConfigCustomizerSrc()
errors.CheckError(err)
cache, err := cacheSrc()
errors.CheckError(err)
metricsServer := metrics.NewMetricsServer()
cacheutil.CollectMetrics(redisClient, metricsServer)
server, err := reposerver.NewServer(metricsServer, cache, tlsConfigCustomizer, parallelismLimit)
errors.CheckError(err)
grpc := server.CreateGRPC()
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", listenPort))
errors.CheckError(err)
http.Handle("/metrics", metricsServer.GetHandler())
go func() { errors.CheckError(http.ListenAndServe(fmt.Sprintf(":%d", metricsPort), nil)) }()
if gpg.IsGPGEnabled() {
log.Infof("Initializing GnuPG keyring at %s", common.GetGnuPGHomePath())
err = gpg.InitializeGnuPG()
errors.CheckError(err)
log.Infof("Populating GnuPG keyring with keys from %s", getGnuPGSourcePath())
added, removed, err := gpg.SyncKeyRingFromDirectory(getGnuPGSourcePath())
errors.CheckError(err)
log.Infof("Loaded %d (and removed %d) keys from keyring", len(added), len(removed))
go func() { errors.CheckError(reposerver.StartGPGWatcher(getGnuPGSourcePath())) }()
}
log.Infof("argocd-repo-server %s serving on %s", common.GetVersion(), listener.Addr())
stats.RegisterStackDumper()
stats.StartStatsTicker(10 * time.Minute)
stats.RegisterHeapDumper("memprofile")
err = grpc.Serve(listener)
errors.CheckError(err)
return nil
},
}
command.Flags().StringVar(&logFormat, "logformat", "text", "Set the logging format. One of: text|json")
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
command.Flags().Int64Var(&parallelismLimit, "parallelismlimit", 0, "Limit on number of concurrent manifests generate requests. Any value less the 1 means no limit.")
command.Flags().IntVar(&listenPort, "port", common.DefaultPortRepoServer, "Listen on given port for incoming connections")
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortRepoServerMetrics, "Start metrics server on given port")
tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command)
cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
redisClient = client
})
return &command
}
func main() {
if err := newCommand().Execute(); err != nil {
if err := commands.NewCommand().Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}

View File

@@ -4,15 +4,13 @@ import (
"context"
"time"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
"github.com/argoproj/pkg/stats"
"github.com/go-redis/redis"
"github.com/go-redis/redis/v8"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
log "github.com/sirupsen/logrus"
"github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
@@ -21,6 +19,7 @@ import (
servercache "github.com/argoproj/argo-cd/server/cache"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/env"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/kube"
"github.com/argoproj/argo-cd/util/tls"
)
@@ -58,14 +57,16 @@ func NewCommand() *cobra.Command {
repoServerAddress string
dexServerAddress string
disableAuth bool
enableGZip bool
tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error)
cacheSrc func() (*servercache.Cache, error)
frameOptions string
)
var command = &cobra.Command{
Use: cliName,
Short: "Run the argocd API server",
Long: "Run the argocd API server",
Use: cliName,
Short: "Run the ArgoCD API server",
Long: "The API server is a gRPC/REST server which exposes the API consumed by the Web UI, CLI, and CI/CD systems. This command runs API server in the foreground. It can be configured by following options.",
DisableAutoGenTag: true,
Run: func(c *cobra.Command, args []string) {
cli.SetLogFormat(logFormat)
cli.SetLogLevel(logLevel)
@@ -115,6 +116,7 @@ func NewCommand() *cobra.Command {
RepoClientset: repoclientset,
DexServerAddr: dexServerAddress,
DisableAuth: disableAuth,
EnableGZip: enableGZip,
TLSConfigCustomizer: tlsConfigCustomizer,
Cache: cache,
XFrameOptions: frameOptions,
@@ -146,6 +148,7 @@ func NewCommand() *cobra.Command {
command.Flags().StringVar(&repoServerAddress, "repo-server", common.DefaultRepoServerAddr, "Repo server address")
command.Flags().StringVar(&dexServerAddress, "dex-server", common.DefaultDexServerAddr, "Dex server address")
command.Flags().BoolVar(&disableAuth, "disable-auth", false, "Disable client authentication")
command.Flags().BoolVar(&enableGZip, "enable-gzip", false, "Enable GZIP compression")
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

@@ -1,14 +1,15 @@
package main
import (
"github.com/argoproj/gitops-engine/pkg/utils/errors"
commands "github.com/argoproj/argo-cd/cmd/argocd-server/commands"
"github.com/argoproj/argo-cd/util/errors"
// load the gcp plugin (required to authenticate against GKE clusters).
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
// load the oidc plugin (required to authenticate with OpenID Connect).
_ "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"
)
func main() {

View File

@@ -5,13 +5,11 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"sort"
"time"
"github.com/argoproj/gitops-engine/pkg/diff"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/ghodss/yaml"
"github.com/spf13/cobra"
apiv1 "k8s.io/api/core/v1"
@@ -33,13 +31,15 @@ import (
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/config"
"github.com/argoproj/argo-cd/util/db"
"github.com/argoproj/argo-cd/util/errors"
kubeutil "github.com/argoproj/argo-cd/util/kube"
"github.com/argoproj/argo-cd/util/settings"
)
func NewAppsCommand() *cobra.Command {
var command = &cobra.Command{
Use: "apps",
Use: "apps",
Short: "Utility commands operate on ArgoCD applications",
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
},
@@ -147,7 +147,7 @@ func diffReconcileResults(res1 reconcileResults, res2 reconcileResults) error {
})
for _, item := range pairs {
printLine(item.name)
_ = diff.PrintDiff(item.name, item.first, item.second)
_ = cli.PrintDiff(item.name, item.first, item.second)
}
return nil
@@ -277,16 +277,22 @@ func reconcileApplications(
appLister := appInformerFactory.Argoproj().V1alpha1().Applications().Lister()
projLister := appInformerFactory.Argoproj().V1alpha1().AppProjects().Lister()
server := metrics.NewMetricsServer("", appLister, func() error {
server, err := metrics.NewMetricsServer("", appLister, func(obj interface{}) bool {
return true
}, func(r *http.Request) error {
return nil
})
if err != nil {
return nil, err
}
stateCache := createLiveStateCache(argoDB, appInformer, settingsMgr, server)
if err := stateCache.Init(); err != nil {
return nil, err
}
appStateManager := controller.NewAppStateManager(
argoDB, appClientset, repoServerClient, namespace, &kube.KubectlCmd{}, settingsMgr, stateCache, projInformer, server)
argoDB, appClientset, repoServerClient, namespace, kubeutil.NewKubectl(), settingsMgr, stateCache, projInformer, server)
appsList, err := appClientset.ArgoprojV1alpha1().Applications(namespace).List(context.Background(), v1.ListOptions{LabelSelector: selector})
if err != nil {
@@ -328,5 +334,5 @@ func reconcileApplications(
}
func newLiveStateCache(argoDB db.ArgoDB, appInformer kubecache.SharedIndexInformer, settingsMgr *settings.SettingsManager, server *metrics.MetricsServer) cache.LiveStateCache {
return cache.NewLiveStateCache(argoDB, appInformer, settingsMgr, &kube.KubectlCmd{}, server, func(managedByApp map[string]bool, ref apiv1.ObjectReference) {})
return cache.NewLiveStateCache(argoDB, appInformer, settingsMgr, kubeutil.NewKubectl(), server, func(managedByApp map[string]bool, ref apiv1.ObjectReference) {}, nil)
}

View File

@@ -0,0 +1,675 @@
package commands
import (
"bufio"
"context"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"reflect"
"syscall"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/ghodss/yaml"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/db"
"github.com/argoproj/argo-cd/util/dex"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/settings"
)
const (
// CLIName is the name of the CLI
cliName = "argocd-util"
// YamlSeparator separates sections of a YAML file
yamlSeparator = "---\n"
)
var (
configMapResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}
secretResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}
applicationsResource = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "applications"}
appprojectsResource = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "appprojects"}
)
// NewCommand returns a new instance of an argocd command
func NewCommand() *cobra.Command {
var (
logFormat string
logLevel string
)
var command = &cobra.Command{
Use: cliName,
Short: "argocd-util tools used by Argo CD",
Long: "argocd-util has internal utility tools used by Argo CD",
DisableAutoGenTag: true,
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
},
}
command.AddCommand(cli.NewVersionCmd(cliName))
command.AddCommand(NewRunDexCommand())
command.AddCommand(NewGenDexConfigCommand())
command.AddCommand(NewImportCommand())
command.AddCommand(NewExportCommand())
command.AddCommand(NewClusterConfig())
command.AddCommand(NewProjectsCommand())
command.AddCommand(NewSettingsCommand())
command.AddCommand(NewAppsCommand())
command.Flags().StringVar(&logFormat, "logformat", "text", "Set the logging format. One of: text|json")
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
return command
}
func NewRunDexCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
)
var command = cobra.Command{
Use: "rundex",
Short: "Runs dex generating a config using settings from the Argo CD configmap and secret",
RunE: func(c *cobra.Command, args []string) error {
_, err := exec.LookPath("dex")
errors.CheckError(err)
config, err := clientConfig.ClientConfig()
errors.CheckError(err)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
kubeClientset := kubernetes.NewForConfigOrDie(config)
settingsMgr := settings.NewSettingsManager(context.Background(), kubeClientset, namespace)
prevSettings, err := settingsMgr.GetSettings()
errors.CheckError(err)
updateCh := make(chan *settings.ArgoCDSettings, 1)
settingsMgr.Subscribe(updateCh)
for {
var cmd *exec.Cmd
dexCfgBytes, err := dex.GenerateDexConfigYAML(prevSettings)
errors.CheckError(err)
if len(dexCfgBytes) == 0 {
log.Infof("dex is not configured")
} else {
err = ioutil.WriteFile("/tmp/dex.yaml", dexCfgBytes, 0644)
errors.CheckError(err)
log.Debug(redactor(string(dexCfgBytes)))
cmd = exec.Command("dex", "serve", "/tmp/dex.yaml")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Start()
errors.CheckError(err)
}
// loop until the dex config changes
for {
newSettings := <-updateCh
newDexCfgBytes, err := dex.GenerateDexConfigYAML(newSettings)
errors.CheckError(err)
if string(newDexCfgBytes) != string(dexCfgBytes) {
prevSettings = newSettings
log.Infof("dex config modified. restarting dex")
if cmd != nil && cmd.Process != nil {
err = cmd.Process.Signal(syscall.SIGTERM)
errors.CheckError(err)
_, err = cmd.Process.Wait()
errors.CheckError(err)
}
break
} else {
log.Infof("dex config unmodified")
}
}
}
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
return &command
}
func NewGenDexConfigCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
out string
)
var command = cobra.Command{
Use: "gendexcfg",
Short: "Generates a dex config from Argo CD settings",
RunE: func(c *cobra.Command, args []string) error {
config, err := clientConfig.ClientConfig()
errors.CheckError(err)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
kubeClientset := kubernetes.NewForConfigOrDie(config)
settingsMgr := settings.NewSettingsManager(context.Background(), kubeClientset, namespace)
settings, err := settingsMgr.GetSettings()
errors.CheckError(err)
dexCfgBytes, err := dex.GenerateDexConfigYAML(settings)
errors.CheckError(err)
if len(dexCfgBytes) == 0 {
log.Infof("dex is not configured")
return nil
}
if out == "" {
dexCfg := make(map[string]interface{})
err := yaml.Unmarshal(dexCfgBytes, &dexCfg)
errors.CheckError(err)
if staticClientsInterface, ok := dexCfg["staticClients"]; ok {
if staticClients, ok := staticClientsInterface.([]interface{}); ok {
for i := range staticClients {
staticClient := staticClients[i]
if mappings, ok := staticClient.(map[string]interface{}); ok {
for key := range mappings {
if key == "secret" {
mappings[key] = "******"
}
}
staticClients[i] = mappings
}
}
dexCfg["staticClients"] = staticClients
}
}
errors.CheckError(err)
maskedDexCfgBytes, err := yaml.Marshal(dexCfg)
errors.CheckError(err)
fmt.Print(string(maskedDexCfgBytes))
} else {
err = ioutil.WriteFile(out, dexCfgBytes, 0644)
errors.CheckError(err)
}
return nil
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().StringVarP(&out, "out", "o", "", "Output to the specified file instead of stdout")
return &command
}
// NewImportCommand defines a new command for exporting Kubernetes and Argo CD resources.
func NewImportCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
prune bool
dryRun bool
)
var command = cobra.Command{
Use: "import SOURCE",
Short: "Import Argo CD data from stdin (specify `-') or a file",
Run: func(c *cobra.Command, args []string) {
if len(args) != 1 {
c.HelpFunc()(c, args)
os.Exit(1)
}
config, err := clientConfig.ClientConfig()
errors.CheckError(err)
config.QPS = 100
config.Burst = 50
errors.CheckError(err)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
acdClients := newArgoCDClientsets(config, namespace)
var input []byte
if in := args[0]; in == "-" {
input, err = ioutil.ReadAll(os.Stdin)
} else {
input, err = ioutil.ReadFile(in)
}
errors.CheckError(err)
var dryRunMsg string
if dryRun {
dryRunMsg = " (dry run)"
}
// pruneObjects tracks live objects and it's current resource version. any remaining
// items in this map indicates the resource should be pruned since it no longer appears
// in the backup
pruneObjects := make(map[kube.ResourceKey]unstructured.Unstructured)
configMaps, err := acdClients.configMaps.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
// referencedSecrets holds any secrets referenced in the argocd-cm configmap. These
// secrets need to be imported too
var referencedSecrets map[string]bool
for _, cm := range configMaps.Items {
if isArgoCDConfigMap(cm.GetName()) {
pruneObjects[kube.ResourceKey{Group: "", Kind: "ConfigMap", Name: cm.GetName()}] = cm
}
if cm.GetName() == common.ArgoCDConfigMapName {
referencedSecrets = getReferencedSecrets(cm)
}
}
secrets, err := acdClients.secrets.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
for _, secret := range secrets.Items {
if isArgoCDSecret(referencedSecrets, secret) {
pruneObjects[kube.ResourceKey{Group: "", Kind: "Secret", Name: secret.GetName()}] = secret
}
}
applications, err := acdClients.applications.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
for _, app := range applications.Items {
pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "Application", Name: app.GetName()}] = app
}
projects, err := acdClients.projects.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
for _, proj := range projects.Items {
pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "AppProject", Name: proj.GetName()}] = proj
}
// Create or replace existing object
backupObjects, err := kube.SplitYAML(input)
errors.CheckError(err)
for _, bakObj := range backupObjects {
gvk := bakObj.GroupVersionKind()
key := kube.ResourceKey{Group: gvk.Group, Kind: gvk.Kind, Name: bakObj.GetName()}
liveObj, exists := pruneObjects[key]
delete(pruneObjects, key)
var dynClient dynamic.ResourceInterface
switch bakObj.GetKind() {
case "Secret":
dynClient = acdClients.secrets
case "ConfigMap":
dynClient = acdClients.configMaps
case "AppProject":
dynClient = acdClients.projects
case "Application":
dynClient = acdClients.applications
}
if !exists {
if !dryRun {
_, err = dynClient.Create(context.Background(), bakObj, metav1.CreateOptions{})
errors.CheckError(err)
}
fmt.Printf("%s/%s %s created%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
} else if specsEqual(*bakObj, liveObj) {
fmt.Printf("%s/%s %s unchanged%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
} else {
if !dryRun {
newLive := updateLive(bakObj, &liveObj)
_, err = dynClient.Update(context.Background(), newLive, metav1.UpdateOptions{})
errors.CheckError(err)
}
fmt.Printf("%s/%s %s updated%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
}
}
// Delete objects not in backup
for key := range pruneObjects {
if prune {
var dynClient dynamic.ResourceInterface
switch key.Kind {
case "Secret":
dynClient = acdClients.secrets
case "AppProject":
dynClient = acdClients.projects
case "Application":
dynClient = acdClients.applications
default:
log.Fatalf("Unexpected kind '%s' in prune list", key.Kind)
}
if !dryRun {
err = dynClient.Delete(context.Background(), key.Name, metav1.DeleteOptions{})
errors.CheckError(err)
}
fmt.Printf("%s/%s %s pruned%s\n", key.Group, key.Kind, key.Name, dryRunMsg)
} else {
fmt.Printf("%s/%s %s needs pruning\n", key.Group, key.Kind, key.Name)
}
}
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().BoolVar(&dryRun, "dry-run", false, "Print what will be performed")
command.Flags().BoolVar(&prune, "prune", false, "Prune secrets, applications and projects which do not appear in the backup")
return &command
}
type argoCDClientsets struct {
configMaps dynamic.ResourceInterface
secrets dynamic.ResourceInterface
applications dynamic.ResourceInterface
projects dynamic.ResourceInterface
}
func newArgoCDClientsets(config *rest.Config, namespace string) *argoCDClientsets {
dynamicIf, err := dynamic.NewForConfig(config)
errors.CheckError(err)
return &argoCDClientsets{
configMaps: dynamicIf.Resource(configMapResource).Namespace(namespace),
secrets: dynamicIf.Resource(secretResource).Namespace(namespace),
applications: dynamicIf.Resource(applicationsResource).Namespace(namespace),
projects: dynamicIf.Resource(appprojectsResource).Namespace(namespace),
}
}
// NewExportCommand defines a new command for exporting Kubernetes and Argo CD resources.
func NewExportCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
out string
)
var command = cobra.Command{
Use: "export",
Short: "Export all Argo CD data to stdout (default) or a file",
Run: func(c *cobra.Command, args []string) {
config, err := clientConfig.ClientConfig()
errors.CheckError(err)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
var writer io.Writer
if out == "-" {
writer = os.Stdout
} else {
f, err := os.Create(out)
errors.CheckError(err)
bw := bufio.NewWriter(f)
writer = bw
defer func() {
err = bw.Flush()
errors.CheckError(err)
err = f.Close()
errors.CheckError(err)
}()
}
acdClients := newArgoCDClientsets(config, namespace)
acdConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDConfigMapName, metav1.GetOptions{})
errors.CheckError(err)
export(writer, *acdConfigMap)
acdRBACConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDRBACConfigMapName, metav1.GetOptions{})
errors.CheckError(err)
export(writer, *acdRBACConfigMap)
acdKnownHostsConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDKnownHostsConfigMapName, metav1.GetOptions{})
errors.CheckError(err)
export(writer, *acdKnownHostsConfigMap)
acdTLSCertsConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDTLSCertsConfigMapName, metav1.GetOptions{})
errors.CheckError(err)
export(writer, *acdTLSCertsConfigMap)
referencedSecrets := getReferencedSecrets(*acdConfigMap)
secrets, err := acdClients.secrets.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
for _, secret := range secrets.Items {
if isArgoCDSecret(referencedSecrets, secret) {
export(writer, secret)
}
}
projects, err := acdClients.projects.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
for _, proj := range projects.Items {
export(writer, proj)
}
applications, err := acdClients.applications.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
for _, app := range applications.Items {
export(writer, app)
}
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().StringVarP(&out, "out", "o", "-", "Output to the specified file instead of stdout")
return &command
}
// getReferencedSecrets examines the argocd-cm config for any referenced repo secrets and returns a
// map of all referenced secrets.
func getReferencedSecrets(un unstructured.Unstructured) map[string]bool {
var cm apiv1.ConfigMap
err := runtime.DefaultUnstructuredConverter.FromUnstructured(un.Object, &cm)
errors.CheckError(err)
referencedSecrets := make(map[string]bool)
// Referenced repository secrets
if reposRAW, ok := cm.Data["repositories"]; ok {
repos := make([]settings.Repository, 0)
err := yaml.Unmarshal([]byte(reposRAW), &repos)
errors.CheckError(err)
for _, cred := range repos {
if cred.PasswordSecret != nil {
referencedSecrets[cred.PasswordSecret.Name] = true
}
if cred.SSHPrivateKeySecret != nil {
referencedSecrets[cred.SSHPrivateKeySecret.Name] = true
}
if cred.UsernameSecret != nil {
referencedSecrets[cred.UsernameSecret.Name] = true
}
if cred.TLSClientCertDataSecret != nil {
referencedSecrets[cred.TLSClientCertDataSecret.Name] = true
}
if cred.TLSClientCertKeySecret != nil {
referencedSecrets[cred.TLSClientCertKeySecret.Name] = true
}
}
}
// Referenced repository credentials secrets
if reposRAW, ok := cm.Data["repository.credentials"]; ok {
creds := make([]settings.RepositoryCredentials, 0)
err := yaml.Unmarshal([]byte(reposRAW), &creds)
errors.CheckError(err)
for _, cred := range creds {
if cred.PasswordSecret != nil {
referencedSecrets[cred.PasswordSecret.Name] = true
}
if cred.SSHPrivateKeySecret != nil {
referencedSecrets[cred.SSHPrivateKeySecret.Name] = true
}
if cred.UsernameSecret != nil {
referencedSecrets[cred.UsernameSecret.Name] = true
}
if cred.TLSClientCertDataSecret != nil {
referencedSecrets[cred.TLSClientCertDataSecret.Name] = true
}
if cred.TLSClientCertKeySecret != nil {
referencedSecrets[cred.TLSClientCertKeySecret.Name] = true
}
}
}
return referencedSecrets
}
// isArgoCDSecret returns whether or not the given secret is a part of Argo CD configuration
// (e.g. argocd-secret, repo credentials, or cluster credentials)
func isArgoCDSecret(repoSecretRefs map[string]bool, un unstructured.Unstructured) bool {
secretName := un.GetName()
if secretName == common.ArgoCDSecretName {
return true
}
if repoSecretRefs != nil {
if _, ok := repoSecretRefs[secretName]; ok {
return true
}
}
if labels := un.GetLabels(); labels != nil {
if _, ok := labels[common.LabelKeySecretType]; ok {
return true
}
}
if annotations := un.GetAnnotations(); annotations != nil {
if annotations[common.AnnotationKeyManagedBy] == common.AnnotationValueManagedByArgoCD {
return true
}
}
return false
}
// isArgoCDConfigMap returns true if the configmap name is one of argo cd's well known configmaps
func isArgoCDConfigMap(name string) bool {
switch name {
case common.ArgoCDConfigMapName, common.ArgoCDRBACConfigMapName, common.ArgoCDKnownHostsConfigMapName, common.ArgoCDTLSCertsConfigMapName:
return true
}
return false
}
// specsEqual returns if the spec, data, labels, annotations, and finalizers of the two
// supplied objects are equal, indicating that no update is necessary during importing
func specsEqual(left, right unstructured.Unstructured) bool {
if !reflect.DeepEqual(left.GetAnnotations(), right.GetAnnotations()) {
return false
}
if !reflect.DeepEqual(left.GetLabels(), right.GetLabels()) {
return false
}
if !reflect.DeepEqual(left.GetFinalizers(), right.GetFinalizers()) {
return false
}
switch left.GetKind() {
case "Secret", "ConfigMap":
leftData, _, _ := unstructured.NestedMap(left.Object, "data")
rightData, _, _ := unstructured.NestedMap(right.Object, "data")
return reflect.DeepEqual(leftData, rightData)
case "AppProject":
leftSpec, _, _ := unstructured.NestedMap(left.Object, "spec")
rightSpec, _, _ := unstructured.NestedMap(right.Object, "spec")
return reflect.DeepEqual(leftSpec, rightSpec)
case "Application":
leftSpec, _, _ := unstructured.NestedMap(left.Object, "spec")
rightSpec, _, _ := unstructured.NestedMap(right.Object, "spec")
leftStatus, _, _ := unstructured.NestedMap(left.Object, "status")
rightStatus, _, _ := unstructured.NestedMap(right.Object, "status")
// reconciledAt and observedAt are constantly changing and we ignore any diff there
delete(leftStatus, "reconciledAt")
delete(rightStatus, "reconciledAt")
delete(leftStatus, "observedAt")
delete(rightStatus, "observedAt")
return reflect.DeepEqual(leftSpec, rightSpec) && reflect.DeepEqual(leftStatus, rightStatus)
}
return false
}
// updateLive replaces the live object's finalizers, spec, annotations, labels, and data from the
// backup object but leaves all other fields intact (status, other metadata, etc...)
func updateLive(bak, live *unstructured.Unstructured) *unstructured.Unstructured {
newLive := live.DeepCopy()
newLive.SetAnnotations(bak.GetAnnotations())
newLive.SetLabels(bak.GetLabels())
newLive.SetFinalizers(bak.GetFinalizers())
switch live.GetKind() {
case "Secret", "ConfigMap":
newLive.Object["data"] = bak.Object["data"]
case "AppProject":
newLive.Object["spec"] = bak.Object["spec"]
case "Application":
newLive.Object["spec"] = bak.Object["spec"]
if _, ok := bak.Object["status"]; ok {
newLive.Object["status"] = bak.Object["status"]
}
}
return newLive
}
// export writes the unstructured object and removes extraneous cruft from output before writing
func export(w io.Writer, un unstructured.Unstructured) {
name := un.GetName()
finalizers := un.GetFinalizers()
apiVersion := un.GetAPIVersion()
kind := un.GetKind()
labels := un.GetLabels()
annotations := un.GetAnnotations()
unstructured.RemoveNestedField(un.Object, "metadata")
un.SetName(name)
un.SetFinalizers(finalizers)
un.SetAPIVersion(apiVersion)
un.SetKind(kind)
un.SetLabels(labels)
un.SetAnnotations(annotations)
data, err := yaml.Marshal(un.Object)
errors.CheckError(err)
_, err = w.Write(data)
errors.CheckError(err)
_, err = w.Write([]byte(yamlSeparator))
errors.CheckError(err)
}
// NewClusterConfig returns a new instance of `argocd-util kubeconfig` command
func NewClusterConfig() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
)
var command = &cobra.Command{
Use: "kubeconfig CLUSTER_URL OUTPUT_PATH",
Short: "Generates kubeconfig for the specified cluster",
DisableAutoGenTag: true,
Run: func(c *cobra.Command, args []string) {
if len(args) != 2 {
c.HelpFunc()(c, args)
os.Exit(1)
}
serverUrl := args[0]
output := args[1]
conf, err := clientConfig.ClientConfig()
errors.CheckError(err)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
kubeclientset, err := kubernetes.NewForConfig(conf)
errors.CheckError(err)
cluster, err := db.NewDB(namespace, settings.NewSettingsManager(context.Background(), kubeclientset, namespace), kubeclientset).GetCluster(context.Background(), serverUrl)
errors.CheckError(err)
err = kube.WriteKubeConfig(cluster.RawRestConfig(), namespace, output)
errors.CheckError(err)
},
}
clientConfig = cli.AddKubectlFlagsToCmd(command)
return command
}
func iterateStringFields(obj interface{}, callback func(name string, val string) string) {
if mapField, ok := obj.(map[string]interface{}); ok {
for field, val := range mapField {
if strVal, ok := val.(string); ok {
mapField[field] = callback(field, strVal)
} else {
iterateStringFields(val, callback)
}
}
} else if arrayField, ok := obj.([]interface{}); ok {
for i := range arrayField {
iterateStringFields(arrayField[i], callback)
}
}
}
func redactor(dirtyString string) string {
config := make(map[string]interface{})
err := yaml.Unmarshal([]byte(dirtyString), &config)
errors.CheckError(err)
iterateStringFields(config, func(name string, val string) string {
if name == "clientSecret" || name == "secret" || name == "bindPW" {
return "********"
} else {
return val
}
})
data, err := yaml.Marshal(config)
errors.CheckError(err)
return string(data)
}

View File

@@ -0,0 +1,144 @@
package commands
import (
"bufio"
"io"
"io/ioutil"
"os"
"strings"
"github.com/ghodss/yaml"
"github.com/spf13/cobra"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/clientcmd"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/cli"
// load the gcp plugin (required to authenticate against GKE clusters).
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
// load the oidc plugin (required to authenticate with OpenID Connect).
_ "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"
)
// NewProjectAllowListGenCommand generates a project from clusterRole
func NewProjectAllowListGenCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
out string
)
var command = &cobra.Command{
Use: "generate-allow-list CLUSTERROLE_PATH PROJ_NAME",
Short: "Generates project allow list from the specified clusterRole file",
Run: func(c *cobra.Command, args []string) {
if len(args) != 2 {
c.HelpFunc()(c, args)
os.Exit(1)
}
clusterRoleFileName := args[0]
projName := args[1]
var writer io.Writer
if out == "-" {
writer = os.Stdout
} else {
f, err := os.Create(out)
errors.CheckError(err)
bw := bufio.NewWriter(f)
writer = bw
defer func() {
err = bw.Flush()
errors.CheckError(err)
err = f.Close()
errors.CheckError(err)
}()
}
globalProj := generateProjectAllowList(clientConfig, clusterRoleFileName, projName)
yamlBytes, err := yaml.Marshal(globalProj)
errors.CheckError(err)
_, err = writer.Write(yamlBytes)
errors.CheckError(err)
},
}
clientConfig = cli.AddKubectlFlagsToCmd(command)
command.Flags().StringVarP(&out, "out", "o", "-", "Output to the specified file instead of stdout")
return command
}
func generateProjectAllowList(clientConfig clientcmd.ClientConfig, clusterRoleFileName string, projName string) v1alpha1.AppProject {
yamlBytes, err := ioutil.ReadFile(clusterRoleFileName)
errors.CheckError(err)
var obj unstructured.Unstructured
err = yaml.Unmarshal(yamlBytes, &obj)
errors.CheckError(err)
clusterRole := &rbacv1.ClusterRole{}
err = scheme.Scheme.Convert(&obj, clusterRole, nil)
errors.CheckError(err)
config, err := clientConfig.ClientConfig()
errors.CheckError(err)
disco, err := discovery.NewDiscoveryClientForConfig(config)
errors.CheckError(err)
serverResources, err := disco.ServerPreferredResources()
errors.CheckError(err)
resourceList := make([]metav1.GroupKind, 0)
for _, rule := range clusterRole.Rules {
if len(rule.APIGroups) <= 0 {
continue
}
canCreate := false
for _, verb := range rule.Verbs {
if strings.EqualFold(verb, "Create") {
canCreate = true
break
}
}
if !canCreate {
continue
}
ruleApiGroup := rule.APIGroups[0]
for _, ruleResource := range rule.Resources {
for _, apiResourcesList := range serverResources {
gv, err := schema.ParseGroupVersion(apiResourcesList.GroupVersion)
if err != nil {
gv = schema.GroupVersion{}
}
if ruleApiGroup == gv.Group {
for _, apiResource := range apiResourcesList.APIResources {
if apiResource.Name == ruleResource {
resourceList = append(resourceList, metav1.GroupKind{Group: ruleApiGroup, Kind: apiResource.Kind})
}
}
}
}
}
}
globalProj := v1alpha1.AppProject{
TypeMeta: metav1.TypeMeta{
Kind: "AppProject",
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{Name: projName},
Spec: v1alpha1.AppProjectSpec{},
}
globalProj.Spec.NamespaceResourceWhitelist = resourceList
return globalProj
}

View File

@@ -0,0 +1,57 @@
package commands
import (
"reflect"
"testing"
"github.com/stretchr/testify/assert"
"github.com/undefinedlabs/go-mpatch"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/discovery"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)
func TestProjectAllowListGen(t *testing.T) {
useMock := true
rules := clientcmd.NewDefaultClientConfigLoadingRules()
overrides := &clientcmd.ConfigOverrides{}
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, overrides)
if useMock {
var patchClientConfig *mpatch.Patch
patchClientConfig, err := mpatch.PatchInstanceMethodByName(reflect.TypeOf(clientConfig), "ClientConfig", func(*clientcmd.DeferredLoadingClientConfig) (*restclient.Config, error) {
return nil, nil
})
assert.NoError(t, err)
patch, err := mpatch.PatchMethod(discovery.NewDiscoveryClientForConfig, func(c *restclient.Config) (*discovery.DiscoveryClient, error) {
return &discovery.DiscoveryClient{LegacyPrefix: "/api"}, nil
})
assert.NoError(t, err)
var patchSeverPreferedResources *mpatch.Patch
discoClient := &discovery.DiscoveryClient{}
patchSeverPreferedResources, err = mpatch.PatchInstanceMethodByName(reflect.TypeOf(discoClient), "ServerPreferredResources", func(*discovery.DiscoveryClient) ([]*metav1.APIResourceList, error) {
res := metav1.APIResource{
Name: "services",
Kind: "Service",
}
resourceList := []*metav1.APIResourceList{{APIResources: []metav1.APIResource{res}}}
return resourceList, nil
})
assert.NoError(t, err)
defer func() {
err = patchClientConfig.Unpatch()
assert.NoError(t, err)
err = patch.Unpatch()
assert.NoError(t, err)
err = patchSeverPreferedResources.Unpatch()
err = patch.Unpatch()
}()
}
globalProj := generateProjectAllowList(clientConfig, "testdata/test_clusterrole.yaml", "testproj")
assert.True(t, len(globalProj.Spec.NamespaceResourceWhitelist) > 0)
}

View File

@@ -11,9 +11,8 @@ import (
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
appclient "github.com/argoproj/argo-cd/pkg/client/clientset/versioned/typed/application/v1alpha1"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/gitops-engine/pkg/diff"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/spf13/cobra"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -22,13 +21,15 @@ import (
func NewProjectsCommand() *cobra.Command {
var command = &cobra.Command{
Use: "projects",
Use: "projects",
Short: "Utility commands operate on ArgoCD Projects",
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
},
}
command.AddCommand(NewUpdatePolicyRuleCommand())
command.AddCommand(NewProjectAllowListGenCommand())
return command
}
@@ -70,7 +71,7 @@ func saveProject(updated v1alpha1.AppProject, orig v1alpha1.AppProject, projects
if err != nil {
return err
}
_ = diff.PrintDiff(updated.Name, target, live)
_ = cli.PrintDiff(updated.Name, target, live)
if !dryRun {
_, err = projectsIf.Update(context.Background(), &updated, v1.UpdateOptions{})
if err != nil {

View File

@@ -1,4 +1,4 @@
package main
package commands
import (
"testing"

View File

@@ -12,9 +12,7 @@ import (
"strings"
"text/tabwriter"
"github.com/argoproj/gitops-engine/pkg/diff"
healthutil "github.com/argoproj/gitops-engine/pkg/health"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
"github.com/ghodss/yaml"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -29,6 +27,7 @@ import (
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/argo/normalizers"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/lua"
"github.com/argoproj/argo-cd/util/settings"
)
@@ -423,7 +422,7 @@ argocd-util settings resource-overrides ignore-differences ./deploy.yaml --argoc
}
_, _ = fmt.Printf("Following fields are ignored:\n\n")
_ = diff.PrintDiff(res.GetName(), &res, normalizedRes)
_ = cli.PrintDiff(res.GetName(), &res, normalizedRes)
})
},
}
@@ -538,7 +537,7 @@ argocd-util settings resource-overrides action run /tmp/deploy.yaml restart --ar
}
_, _ = fmt.Printf("Following fields have been changed:\n\n")
_ = diff.PrintDiff(res.GetName(), &res, modifiedRes)
_ = cli.PrintDiff(res.GetName(), &res, modifiedRes)
})
},
}

View File

@@ -10,9 +10,9 @@ import (
"testing"
"github.com/argoproj/argo-cd/common"
utils "github.com/argoproj/argo-cd/util/io"
"github.com/argoproj/argo-cd/util/settings"
utils "github.com/argoproj/gitops-engine/pkg/utils/io"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

View File

@@ -0,0 +1,787 @@
aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.authorization.k8s.io/aggregate-to-admin: "true"
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: admin
rules:
- apiGroups:
- argoproj.io
resources:
- workflows
- workflows/finalizers
- workflowtemplates
- workflowtemplates/finalizers
- cronworkflows
- cronworkflows/finalizers
- clusterworkflowtemplates
- clusterworkflowtemplates/finalizers
verbs:
- create
- delete
- deletecollection
- get
- list
- patch
- update
- watch
- apiGroups:
- argoproj.io
resources:
- gateways
- gateways/finalizers
- sensors
- sensors/finalizers
- eventsources
- eventsources/finalizers
- eventbuses
- eventbuses/finalizers
verbs:
- create
- delete
- deletecollection
- get
- list
- patch
- update
- watch
- apiGroups:
- argoproj.io
resources:
- rollouts
- rollouts/scale
- experiments
- analysistemplates
- clusteranalysistemplates
- analysisruns
verbs:
- create
- delete
- deletecollection
- get
- list
- patch
- update
- watch
- apiGroups:
- metrics.k8s.io
resources:
- pods
verbs:
- get
- list
- watch
- apiGroups:
- iammanager.keikoproj.io
resources:
- iamroles
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- pods/attach
- pods/exec
- pods/portforward
- pods/proxy
- secrets
- services/proxy
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- serviceaccounts
verbs:
- impersonate
- apiGroups:
- ""
resources:
- pods
- pods/attach
- pods/exec
- pods/portforward
- pods/proxy
verbs:
- create
- delete
- deletecollection
- patch
- update
- apiGroups:
- ""
resources:
- configmaps
- endpoints
- persistentvolumeclaims
- replicationcontrollers
- replicationcontrollers/scale
- secrets
- serviceaccounts
- services
- services/proxy
verbs:
- create
- delete
- deletecollection
- patch
- update
- apiGroups:
- apps
resources:
- daemonsets
- deployments
- deployments/rollback
- deployments/scale
- replicasets
- replicasets/scale
- statefulsets
- statefulsets/scale
verbs:
- create
- delete
- deletecollection
- patch
- update
- apiGroups:
- autoscaling
resources:
- horizontalpodautoscalers
verbs:
- create
- delete
- deletecollection
- patch
- update
- apiGroups:
- batch
resources:
- cronjobs
- jobs
verbs:
- create
- delete
- deletecollection
- patch
- update
- apiGroups:
- extensions
resources:
- daemonsets
- deployments
- deployments/rollback
- deployments/scale
- ingresses
- networkpolicies
- replicasets
- replicasets/scale
- replicationcontrollers/scale
verbs:
- create
- delete
- deletecollection
- patch
- update
- apiGroups:
- policy
resources:
- poddisruptionbudgets
verbs:
- create
- delete
- deletecollection
- patch
- update
- apiGroups:
- networking.k8s.io
resources:
- networkpolicies
verbs:
- create
- delete
- deletecollection
- patch
- update
- apiGroups:
- networking.k8s.io
resources:
- ingresses
verbs:
- create
- apiGroups:
- networking.k8s.io
resources:
- ingresses
verbs:
- delete
- apiGroups:
- networking.k8s.io
resources:
- ingresses
verbs:
- deletecollection
- apiGroups:
- networking.k8s.io
resources:
- ingresses
verbs:
- patch
- apiGroups:
- networking.k8s.io
resources:
- ingresses
verbs:
- update
- apiGroups:
- argoproj.io
resources:
- workflows
- workflows/finalizers
- workflowtemplates
- workflowtemplates/finalizers
- cronworkflows
- cronworkflows/finalizers
- clusterworkflowtemplates
- clusterworkflowtemplates/finalizers
verbs:
- get
- list
- watch
- apiGroups:
- argoproj.io
resources:
- gateways
- gateways/finalizers
- sensors
- sensors/finalizers
- eventsources
- eventsources/finalizers
- eventbuses
- eventbuses/finalizers
verbs:
- get
- list
- watch
- apiGroups:
- argoproj.io
resources:
- rollouts
- rollouts/scale
- experiments
- analysistemplates
- clusteranalysistemplates
- analysisruns
verbs:
- get
- list
- watch
- apiGroups:
- ""
resourceNames:
- prometheus-k8s-prometheus-1
- prometheus-k8s-prometheus-0
resources:
- pods/portforward
verbs:
- create
- apiGroups:
- networking.istio.io
resources:
- virtualservices
- destinationrules
- serviceentries
- envoyfilters
- gateways
- sidecars
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- configmaps
- endpoints
- persistentvolumeclaims
- pods
- replicationcontrollers
- replicationcontrollers/scale
- serviceaccounts
- services
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- bindings
- events
- limitranges
- namespaces/status
- pods/log
- pods/status
- replicationcontrollers/status
- resourcequotas
- resourcequotas/status
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- namespaces
verbs:
- get
- list
- watch
- apiGroups:
- apps
resources:
- daemonsets
- deployments
- deployments/scale
- replicasets
- replicasets/scale
- statefulsets
- statefulsets/scale
verbs:
- get
- list
- watch
- apiGroups:
- autoscaling
resources:
- horizontalpodautoscalers
verbs:
- get
- list
- watch
- apiGroups:
- batch
resources:
- cronjobs
- jobs
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- daemonsets
- deployments
- deployments/scale
- ingresses
- networkpolicies
- replicasets
- replicasets/scale
- replicationcontrollers/scale
verbs:
- get
- list
- watch
- apiGroups:
- policy
resources:
- poddisruptionbudgets
verbs:
- get
- list
- watch
- apiGroups:
- networking.k8s.io
resources:
- networkpolicies
verbs:
- get
- list
- watch
- apiGroups:
- apps
resources:
- controllerrevisions
verbs:
- get
- apiGroups:
- apps
resources:
- controllerrevisions
verbs:
- list
- apiGroups:
- apps
resources:
- controllerrevisions
verbs:
- watch
- apiGroups:
- networking.k8s.io
resources:
- ingresses
verbs:
- get
- apiGroups:
- networking.k8s.io
resources:
- ingresses
verbs:
- list
- apiGroups:
- networking.k8s.io
resources:
- ingresses
verbs:
- watch
- apiGroups:
- ""
resources:
- persistentvolumeclaims/status
verbs:
- get
- apiGroups:
- ""
resources:
- persistentvolumeclaims/status
verbs:
- list
- apiGroups:
- ""
resources:
- persistentvolumeclaims/status
verbs:
- watch
- apiGroups:
- ""
resources:
- services/status
verbs:
- get
- apiGroups:
- ""
resources:
- services/status
verbs:
- list
- apiGroups:
- ""
resources:
- services/status
verbs:
- watch
- apiGroups:
- apps
resources:
- daemonsets/status
verbs:
- get
- apiGroups:
- apps
resources:
- daemonsets/status
verbs:
- list
- apiGroups:
- apps
resources:
- daemonsets/status
verbs:
- watch
- apiGroups:
- apps
resources:
- deployments/status
verbs:
- get
- apiGroups:
- apps
resources:
- deployments/status
verbs:
- list
- apiGroups:
- apps
resources:
- deployments/status
verbs:
- watch
- apiGroups:
- apps
resources:
- replicasets/status
verbs:
- get
- apiGroups:
- apps
resources:
- replicasets/status
verbs:
- list
- apiGroups:
- apps
resources:
- replicasets/status
verbs:
- watch
- apiGroups:
- apps
resources:
- statefulsets/status
verbs:
- get
- apiGroups:
- apps
resources:
- statefulsets/status
verbs:
- list
- apiGroups:
- apps
resources:
- statefulsets/status
verbs:
- watch
- apiGroups:
- autoscaling
resources:
- horizontalpodautoscalers/status
verbs:
- get
- apiGroups:
- autoscaling
resources:
- horizontalpodautoscalers/status
verbs:
- list
- apiGroups:
- autoscaling
resources:
- horizontalpodautoscalers/status
verbs:
- watch
- apiGroups:
- batch
resources:
- cronjobs/status
verbs:
- get
- apiGroups:
- batch
resources:
- cronjobs/status
verbs:
- list
- apiGroups:
- batch
resources:
- cronjobs/status
verbs:
- watch
- apiGroups:
- batch
resources:
- jobs/status
verbs:
- get
- apiGroups:
- batch
resources:
- jobs/status
verbs:
- list
- apiGroups:
- batch
resources:
- jobs/status
verbs:
- watch
- apiGroups:
- extensions
resources:
- daemonsets/status
verbs:
- get
- apiGroups:
- extensions
resources:
- daemonsets/status
verbs:
- list
- apiGroups:
- extensions
resources:
- daemonsets/status
verbs:
- watch
- apiGroups:
- extensions
resources:
- deployments/status
verbs:
- get
- apiGroups:
- extensions
resources:
- deployments/status
verbs:
- list
- apiGroups:
- extensions
resources:
- deployments/status
verbs:
- watch
- apiGroups:
- extensions
resources:
- ingresses/status
verbs:
- get
- apiGroups:
- extensions
resources:
- ingresses/status
verbs:
- list
- apiGroups:
- extensions
resources:
- ingresses/status
verbs:
- watch
- apiGroups:
- extensions
resources:
- replicasets/status
verbs:
- get
- apiGroups:
- extensions
resources:
- replicasets/status
verbs:
- list
- apiGroups:
- extensions
resources:
- replicasets/status
verbs:
- watch
- apiGroups:
- policy
resources:
- poddisruptionbudgets/status
verbs:
- get
- apiGroups:
- policy
resources:
- poddisruptionbudgets/status
verbs:
- list
- apiGroups:
- policy
resources:
- poddisruptionbudgets/status
verbs:
- watch
- apiGroups:
- networking.k8s.io
resources:
- ingresses/status
verbs:
- get
- apiGroups:
- networking.k8s.io
resources:
- ingresses/status
verbs:
- list
- apiGroups:
- networking.k8s.io
resources:
- ingresses/status
verbs:
- watch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- apiGroups:
- extensions
- networking.k8s.io
resources:
- ingresses/status
verbs:
- update
- apiGroups:
- monitoring.coreos.com
resources:
- prometheusrules
verbs:
- get
- watch
- list
- update
- delete
- create
- apiGroups:
- hpa.orkaproj.io
resources:
- hpaalgoes
verbs:
- get
- watch
- list
- update
- delete
- create
- apiGroups:
- networking.istio.io
resources:
- virtualservices
- destinationrules
- serviceentries
- envoyfilters
- gateways
- sidecars
verbs:
- get
- list
- create
- update
- delete
- patch
- watch
- apiGroups:
- authorization.k8s.io
resources:
- localsubjectaccessreviews
verbs:
- create
- apiGroups:
- rbac.authorization.k8s.io
resources:
- rolebindings
- roles
verbs:
- create
- delete
- deletecollection
- get
- list
- patch
- update
- watch

View File

@@ -1,684 +1,21 @@
package main
import (
"bufio"
"context"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"reflect"
"syscall"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/ghodss/yaml"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"github.com/argoproj/argo-cd/cmd/argocd-util/commands"
"github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/db"
"github.com/argoproj/argo-cd/util/dex"
"github.com/argoproj/argo-cd/util/settings"
// load the gcp plugin (required to authenticate against GKE clusters).
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
// load the oidc plugin (required to authenticate with OpenID Connect).
_ "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"
)
const (
// CLIName is the name of the CLI
cliName = "argocd-util"
// YamlSeparator separates sections of a YAML file
yamlSeparator = "---\n"
)
var (
configMapResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}
secretResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}
applicationsResource = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "applications"}
appprojectsResource = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "appprojects"}
)
// NewCommand returns a new instance of an argocd command
func NewCommand() *cobra.Command {
var (
logFormat string
logLevel string
)
var command = &cobra.Command{
Use: cliName,
Short: "argocd-util has internal tools used by Argo CD",
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
},
}
command.AddCommand(cli.NewVersionCmd(cliName))
command.AddCommand(NewRunDexCommand())
command.AddCommand(NewGenDexConfigCommand())
command.AddCommand(NewImportCommand())
command.AddCommand(NewExportCommand())
command.AddCommand(NewClusterConfig())
command.AddCommand(commands.NewProjectsCommand())
command.AddCommand(commands.NewSettingsCommand())
command.AddCommand(commands.NewAppsCommand())
command.Flags().StringVar(&logFormat, "logformat", "text", "Set the logging format. One of: text|json")
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
return command
}
func NewRunDexCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
)
var command = cobra.Command{
Use: "rundex",
Short: "Runs dex generating a config using settings from the Argo CD configmap and secret",
RunE: func(c *cobra.Command, args []string) error {
_, err := exec.LookPath("dex")
errors.CheckError(err)
config, err := clientConfig.ClientConfig()
errors.CheckError(err)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
kubeClientset := kubernetes.NewForConfigOrDie(config)
settingsMgr := settings.NewSettingsManager(context.Background(), kubeClientset, namespace)
prevSettings, err := settingsMgr.GetSettings()
errors.CheckError(err)
updateCh := make(chan *settings.ArgoCDSettings, 1)
settingsMgr.Subscribe(updateCh)
for {
var cmd *exec.Cmd
dexCfgBytes, err := dex.GenerateDexConfigYAML(prevSettings)
errors.CheckError(err)
if len(dexCfgBytes) == 0 {
log.Infof("dex is not configured")
} else {
err = ioutil.WriteFile("/tmp/dex.yaml", dexCfgBytes, 0644)
errors.CheckError(err)
log.Debug(redactor(string(dexCfgBytes)))
cmd = exec.Command("dex", "serve", "/tmp/dex.yaml")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Start()
errors.CheckError(err)
}
// loop until the dex config changes
for {
newSettings := <-updateCh
newDexCfgBytes, err := dex.GenerateDexConfigYAML(newSettings)
errors.CheckError(err)
if string(newDexCfgBytes) != string(dexCfgBytes) {
prevSettings = newSettings
log.Infof("dex config modified. restarting dex")
if cmd != nil && cmd.Process != nil {
err = cmd.Process.Signal(syscall.SIGTERM)
errors.CheckError(err)
_, err = cmd.Process.Wait()
errors.CheckError(err)
}
break
} else {
log.Infof("dex config unmodified")
}
}
}
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
return &command
}
func NewGenDexConfigCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
out string
)
var command = cobra.Command{
Use: "gendexcfg",
Short: "Generates a dex config from Argo CD settings",
RunE: func(c *cobra.Command, args []string) error {
config, err := clientConfig.ClientConfig()
errors.CheckError(err)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
kubeClientset := kubernetes.NewForConfigOrDie(config)
settingsMgr := settings.NewSettingsManager(context.Background(), kubeClientset, namespace)
settings, err := settingsMgr.GetSettings()
errors.CheckError(err)
dexCfgBytes, err := dex.GenerateDexConfigYAML(settings)
errors.CheckError(err)
if len(dexCfgBytes) == 0 {
log.Infof("dex is not configured")
return nil
}
if out == "" {
dexCfg := make(map[string]interface{})
err := yaml.Unmarshal(dexCfgBytes, &dexCfg)
errors.CheckError(err)
if staticClientsInterface, ok := dexCfg["staticClients"]; ok {
if staticClients, ok := staticClientsInterface.([]interface{}); ok {
for i := range staticClients {
staticClient := staticClients[i]
if mappings, ok := staticClient.(map[string]interface{}); ok {
for key := range mappings {
if key == "secret" {
mappings[key] = "******"
}
}
staticClients[i] = mappings
}
}
dexCfg["staticClients"] = staticClients
}
}
errors.CheckError(err)
maskedDexCfgBytes, err := yaml.Marshal(dexCfg)
errors.CheckError(err)
fmt.Print(string(maskedDexCfgBytes))
} else {
err = ioutil.WriteFile(out, dexCfgBytes, 0644)
errors.CheckError(err)
}
return nil
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().StringVarP(&out, "out", "o", "", "Output to the specified file instead of stdout")
return &command
}
// NewImportCommand defines a new command for exporting Kubernetes and Argo CD resources.
func NewImportCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
prune bool
dryRun bool
)
var command = cobra.Command{
Use: "import SOURCE",
Short: "Import Argo CD data from stdin (specify `-') or a file",
Run: func(c *cobra.Command, args []string) {
if len(args) != 1 {
c.HelpFunc()(c, args)
os.Exit(1)
}
config, err := clientConfig.ClientConfig()
errors.CheckError(err)
config.QPS = 100
config.Burst = 50
errors.CheckError(err)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
acdClients := newArgoCDClientsets(config, namespace)
var input []byte
if in := args[0]; in == "-" {
input, err = ioutil.ReadAll(os.Stdin)
} else {
input, err = ioutil.ReadFile(in)
}
errors.CheckError(err)
var dryRunMsg string
if dryRun {
dryRunMsg = " (dry run)"
}
// pruneObjects tracks live objects and it's current resource version. any remaining
// items in this map indicates the resource should be pruned since it no longer appears
// in the backup
pruneObjects := make(map[kube.ResourceKey]unstructured.Unstructured)
configMaps, err := acdClients.configMaps.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
// referencedSecrets holds any secrets referenced in the argocd-cm configmap. These
// secrets need to be imported too
var referencedSecrets map[string]bool
for _, cm := range configMaps.Items {
if isArgoCDConfigMap(cm.GetName()) {
pruneObjects[kube.ResourceKey{Group: "", Kind: "ConfigMap", Name: cm.GetName()}] = cm
}
if cm.GetName() == common.ArgoCDConfigMapName {
referencedSecrets = getReferencedSecrets(cm)
}
}
secrets, err := acdClients.secrets.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
for _, secret := range secrets.Items {
if isArgoCDSecret(referencedSecrets, secret) {
pruneObjects[kube.ResourceKey{Group: "", Kind: "Secret", Name: secret.GetName()}] = secret
}
}
applications, err := acdClients.applications.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
for _, app := range applications.Items {
pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "Application", Name: app.GetName()}] = app
}
projects, err := acdClients.projects.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
for _, proj := range projects.Items {
pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "AppProject", Name: proj.GetName()}] = proj
}
// Create or replace existing object
backupObjects, err := kube.SplitYAML(input)
errors.CheckError(err)
for _, bakObj := range backupObjects {
gvk := bakObj.GroupVersionKind()
key := kube.ResourceKey{Group: gvk.Group, Kind: gvk.Kind, Name: bakObj.GetName()}
liveObj, exists := pruneObjects[key]
delete(pruneObjects, key)
var dynClient dynamic.ResourceInterface
switch bakObj.GetKind() {
case "Secret":
dynClient = acdClients.secrets
case "ConfigMap":
dynClient = acdClients.configMaps
case "AppProject":
dynClient = acdClients.projects
case "Application":
dynClient = acdClients.applications
}
if !exists {
if !dryRun {
_, err = dynClient.Create(context.Background(), bakObj, metav1.CreateOptions{})
errors.CheckError(err)
}
fmt.Printf("%s/%s %s created%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
} else if specsEqual(*bakObj, liveObj) {
fmt.Printf("%s/%s %s unchanged%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
} else {
if !dryRun {
newLive := updateLive(bakObj, &liveObj)
_, err = dynClient.Update(context.Background(), newLive, metav1.UpdateOptions{})
errors.CheckError(err)
}
fmt.Printf("%s/%s %s updated%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
}
}
// Delete objects not in backup
for key := range pruneObjects {
if prune {
var dynClient dynamic.ResourceInterface
switch key.Kind {
case "Secret":
dynClient = acdClients.secrets
case "AppProject":
dynClient = acdClients.projects
case "Application":
dynClient = acdClients.applications
default:
log.Fatalf("Unexpected kind '%s' in prune list", key.Kind)
}
if !dryRun {
err = dynClient.Delete(context.Background(), key.Name, metav1.DeleteOptions{})
errors.CheckError(err)
}
fmt.Printf("%s/%s %s pruned%s\n", key.Group, key.Kind, key.Name, dryRunMsg)
} else {
fmt.Printf("%s/%s %s needs pruning\n", key.Group, key.Kind, key.Name)
}
}
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().BoolVar(&dryRun, "dry-run", false, "Print what will be performed")
command.Flags().BoolVar(&prune, "prune", false, "Prune secrets, applications and projects which do not appear in the backup")
return &command
}
type argoCDClientsets struct {
configMaps dynamic.ResourceInterface
secrets dynamic.ResourceInterface
applications dynamic.ResourceInterface
projects dynamic.ResourceInterface
}
func newArgoCDClientsets(config *rest.Config, namespace string) *argoCDClientsets {
dynamicIf, err := dynamic.NewForConfig(config)
errors.CheckError(err)
return &argoCDClientsets{
configMaps: dynamicIf.Resource(configMapResource).Namespace(namespace),
secrets: dynamicIf.Resource(secretResource).Namespace(namespace),
applications: dynamicIf.Resource(applicationsResource).Namespace(namespace),
projects: dynamicIf.Resource(appprojectsResource).Namespace(namespace),
}
}
// NewExportCommand defines a new command for exporting Kubernetes and Argo CD resources.
func NewExportCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
out string
)
var command = cobra.Command{
Use: "export",
Short: "Export all Argo CD data to stdout (default) or a file",
Run: func(c *cobra.Command, args []string) {
config, err := clientConfig.ClientConfig()
errors.CheckError(err)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
var writer io.Writer
if out == "-" {
writer = os.Stdout
} else {
f, err := os.Create(out)
errors.CheckError(err)
bw := bufio.NewWriter(f)
writer = bw
defer func() {
err = bw.Flush()
errors.CheckError(err)
err = f.Close()
errors.CheckError(err)
}()
}
acdClients := newArgoCDClientsets(config, namespace)
acdConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDConfigMapName, metav1.GetOptions{})
errors.CheckError(err)
export(writer, *acdConfigMap)
acdRBACConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDRBACConfigMapName, metav1.GetOptions{})
errors.CheckError(err)
export(writer, *acdRBACConfigMap)
acdKnownHostsConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDKnownHostsConfigMapName, metav1.GetOptions{})
errors.CheckError(err)
export(writer, *acdKnownHostsConfigMap)
acdTLSCertsConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDTLSCertsConfigMapName, metav1.GetOptions{})
errors.CheckError(err)
export(writer, *acdTLSCertsConfigMap)
referencedSecrets := getReferencedSecrets(*acdConfigMap)
secrets, err := acdClients.secrets.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
for _, secret := range secrets.Items {
if isArgoCDSecret(referencedSecrets, secret) {
export(writer, secret)
}
}
projects, err := acdClients.projects.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
for _, proj := range projects.Items {
export(writer, proj)
}
applications, err := acdClients.applications.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
for _, app := range applications.Items {
export(writer, app)
}
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().StringVarP(&out, "out", "o", "-", "Output to the specified file instead of stdout")
return &command
}
// getReferencedSecrets examines the argocd-cm config for any referenced repo secrets and returns a
// map of all referenced secrets.
func getReferencedSecrets(un unstructured.Unstructured) map[string]bool {
var cm apiv1.ConfigMap
err := runtime.DefaultUnstructuredConverter.FromUnstructured(un.Object, &cm)
errors.CheckError(err)
referencedSecrets := make(map[string]bool)
// Referenced repository secrets
if reposRAW, ok := cm.Data["repositories"]; ok {
repos := make([]settings.Repository, 0)
err := yaml.Unmarshal([]byte(reposRAW), &repos)
errors.CheckError(err)
for _, cred := range repos {
if cred.PasswordSecret != nil {
referencedSecrets[cred.PasswordSecret.Name] = true
}
if cred.SSHPrivateKeySecret != nil {
referencedSecrets[cred.SSHPrivateKeySecret.Name] = true
}
if cred.UsernameSecret != nil {
referencedSecrets[cred.UsernameSecret.Name] = true
}
if cred.TLSClientCertDataSecret != nil {
referencedSecrets[cred.TLSClientCertDataSecret.Name] = true
}
if cred.TLSClientCertKeySecret != nil {
referencedSecrets[cred.TLSClientCertKeySecret.Name] = true
}
}
}
// Referenced repository credentials secrets
if reposRAW, ok := cm.Data["repository.credentials"]; ok {
creds := make([]settings.RepositoryCredentials, 0)
err := yaml.Unmarshal([]byte(reposRAW), &creds)
errors.CheckError(err)
for _, cred := range creds {
if cred.PasswordSecret != nil {
referencedSecrets[cred.PasswordSecret.Name] = true
}
if cred.SSHPrivateKeySecret != nil {
referencedSecrets[cred.SSHPrivateKeySecret.Name] = true
}
if cred.UsernameSecret != nil {
referencedSecrets[cred.UsernameSecret.Name] = true
}
if cred.TLSClientCertDataSecret != nil {
referencedSecrets[cred.TLSClientCertDataSecret.Name] = true
}
if cred.TLSClientCertKeySecret != nil {
referencedSecrets[cred.TLSClientCertKeySecret.Name] = true
}
}
}
return referencedSecrets
}
// isArgoCDSecret returns whether or not the given secret is a part of Argo CD configuration
// (e.g. argocd-secret, repo credentials, or cluster credentials)
func isArgoCDSecret(repoSecretRefs map[string]bool, un unstructured.Unstructured) bool {
secretName := un.GetName()
if secretName == common.ArgoCDSecretName {
return true
}
if repoSecretRefs != nil {
if _, ok := repoSecretRefs[secretName]; ok {
return true
}
}
if labels := un.GetLabels(); labels != nil {
if _, ok := labels[common.LabelKeySecretType]; ok {
return true
}
}
if annotations := un.GetAnnotations(); annotations != nil {
if annotations[common.AnnotationKeyManagedBy] == common.AnnotationValueManagedByArgoCD {
return true
}
}
return false
}
// isArgoCDConfigMap returns true if the configmap name is one of argo cd's well known configmaps
func isArgoCDConfigMap(name string) bool {
switch name {
case common.ArgoCDConfigMapName, common.ArgoCDRBACConfigMapName, common.ArgoCDKnownHostsConfigMapName, common.ArgoCDTLSCertsConfigMapName:
return true
}
return false
}
// specsEqual returns if the spec, data, labels, annotations, and finalizers of the two
// supplied objects are equal, indicating that no update is necessary during importing
func specsEqual(left, right unstructured.Unstructured) bool {
if !reflect.DeepEqual(left.GetAnnotations(), right.GetAnnotations()) {
return false
}
if !reflect.DeepEqual(left.GetLabels(), right.GetLabels()) {
return false
}
if !reflect.DeepEqual(left.GetFinalizers(), right.GetFinalizers()) {
return false
}
switch left.GetKind() {
case "Secret", "ConfigMap":
leftData, _, _ := unstructured.NestedMap(left.Object, "data")
rightData, _, _ := unstructured.NestedMap(right.Object, "data")
return reflect.DeepEqual(leftData, rightData)
case "AppProject":
leftSpec, _, _ := unstructured.NestedMap(left.Object, "spec")
rightSpec, _, _ := unstructured.NestedMap(right.Object, "spec")
return reflect.DeepEqual(leftSpec, rightSpec)
case "Application":
leftSpec, _, _ := unstructured.NestedMap(left.Object, "spec")
rightSpec, _, _ := unstructured.NestedMap(right.Object, "spec")
leftStatus, _, _ := unstructured.NestedMap(left.Object, "status")
rightStatus, _, _ := unstructured.NestedMap(right.Object, "status")
// reconciledAt and observedAt are constantly changing and we ignore any diff there
delete(leftStatus, "reconciledAt")
delete(rightStatus, "reconciledAt")
delete(leftStatus, "observedAt")
delete(rightStatus, "observedAt")
return reflect.DeepEqual(leftSpec, rightSpec) && reflect.DeepEqual(leftStatus, rightStatus)
}
return false
}
// updateLive replaces the live object's finalizers, spec, annotations, labels, and data from the
// backup object but leaves all other fields intact (status, other metadata, etc...)
func updateLive(bak, live *unstructured.Unstructured) *unstructured.Unstructured {
newLive := live.DeepCopy()
newLive.SetAnnotations(bak.GetAnnotations())
newLive.SetLabels(bak.GetLabels())
newLive.SetFinalizers(bak.GetFinalizers())
switch live.GetKind() {
case "Secret", "ConfigMap":
newLive.Object["data"] = bak.Object["data"]
case "AppProject":
newLive.Object["spec"] = bak.Object["spec"]
case "Application":
newLive.Object["spec"] = bak.Object["spec"]
if _, ok := bak.Object["status"]; ok {
newLive.Object["status"] = bak.Object["status"]
}
}
return newLive
}
// export writes the unstructured object and removes extraneous cruft from output before writing
func export(w io.Writer, un unstructured.Unstructured) {
name := un.GetName()
finalizers := un.GetFinalizers()
apiVersion := un.GetAPIVersion()
kind := un.GetKind()
labels := un.GetLabels()
annotations := un.GetAnnotations()
unstructured.RemoveNestedField(un.Object, "metadata")
un.SetName(name)
un.SetFinalizers(finalizers)
un.SetAPIVersion(apiVersion)
un.SetKind(kind)
un.SetLabels(labels)
un.SetAnnotations(annotations)
data, err := yaml.Marshal(un.Object)
errors.CheckError(err)
_, err = w.Write(data)
errors.CheckError(err)
_, err = w.Write([]byte(yamlSeparator))
errors.CheckError(err)
}
// NewClusterConfig returns a new instance of `argocd-util kubeconfig` command
func NewClusterConfig() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
)
var command = &cobra.Command{
Use: "kubeconfig CLUSTER_URL OUTPUT_PATH",
Short: "Generates kubeconfig for the specified cluster",
Run: func(c *cobra.Command, args []string) {
if len(args) != 2 {
c.HelpFunc()(c, args)
os.Exit(1)
}
serverUrl := args[0]
output := args[1]
conf, err := clientConfig.ClientConfig()
errors.CheckError(err)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
kubeclientset, err := kubernetes.NewForConfig(conf)
errors.CheckError(err)
cluster, err := db.NewDB(namespace, settings.NewSettingsManager(context.Background(), kubeclientset, namespace), kubeclientset).GetCluster(context.Background(), serverUrl)
errors.CheckError(err)
err = kube.WriteKubeConfig(cluster.RawRestConfig(), namespace, output)
errors.CheckError(err)
},
}
clientConfig = cli.AddKubectlFlagsToCmd(command)
return command
}
func iterateStringFields(obj interface{}, callback func(name string, val string) string) {
if mapField, ok := obj.(map[string]interface{}); ok {
for field, val := range mapField {
if strVal, ok := val.(string); ok {
mapField[field] = callback(field, strVal)
} else {
iterateStringFields(val, callback)
}
}
} else if arrayField, ok := obj.([]interface{}); ok {
for i := range arrayField {
iterateStringFields(arrayField[i], callback)
}
}
}
func redactor(dirtyString string) string {
config := make(map[string]interface{})
err := yaml.Unmarshal([]byte(dirtyString), &config)
errors.CheckError(err)
iterateStringFields(config, func(name string, val string) string {
if name == "clientSecret" || name == "secret" || name == "bindPW" {
return "********"
} else {
return val
}
})
data, err := yaml.Marshal(config)
errors.CheckError(err)
return string(data)
}
func main() {
if err := NewCommand().Execute(); err != nil {
if err := commands.NewCommand().Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}

View File

@@ -10,8 +10,6 @@ import (
"text/tabwriter"
"time"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
"github.com/argoproj/gitops-engine/pkg/utils/io"
timeutil "github.com/argoproj/pkg/time"
"github.com/ghodss/yaml"
log "github.com/sirupsen/logrus"
@@ -23,6 +21,8 @@ import (
"github.com/argoproj/argo-cd/pkg/apiclient/session"
"github.com/argoproj/argo-cd/server/rbacpolicy"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/io"
"github.com/argoproj/argo-cd/util/localconfig"
sessionutil "github.com/argoproj/argo-cd/util/session"
)

View File

@@ -20,8 +20,6 @@ import (
"github.com/argoproj/gitops-engine/pkg/health"
"github.com/argoproj/gitops-engine/pkg/sync/hook"
"github.com/argoproj/gitops-engine/pkg/sync/ignore"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
argoio "github.com/argoproj/gitops-engine/pkg/utils/io"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/ghodss/yaml"
log "github.com/sirupsen/logrus"
@@ -37,9 +35,11 @@ import (
"github.com/argoproj/argo-cd/controller"
"github.com/argoproj/argo-cd/pkg/apiclient"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
"github.com/argoproj/argo-cd/pkg/apiclient/application"
applicationpkg "github.com/argoproj/argo-cd/pkg/apiclient/application"
clusterpkg "github.com/argoproj/argo-cd/pkg/apiclient/cluster"
projectpkg "github.com/argoproj/argo-cd/pkg/apiclient/project"
"github.com/argoproj/argo-cd/pkg/apiclient/settings"
settingspkg "github.com/argoproj/argo-cd/pkg/apiclient/settings"
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
repoapiclient "github.com/argoproj/argo-cd/reposerver/apiclient"
@@ -47,7 +47,9 @@ import (
"github.com/argoproj/argo-cd/util/argo"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/config"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/git"
argoio "github.com/argoproj/argo-cd/util/io"
argokube "github.com/argoproj/argo-cd/util/kube"
"github.com/argoproj/argo-cd/util/templates"
"github.com/argoproj/argo-cd/util/text/label"
@@ -123,7 +125,7 @@ func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.
argocd app create nginx-ingress --repo https://kubernetes-charts.storage.googleapis.com --helm-chart nginx-ingress --revision 1.24.3 --dest-namespace default --dest-server https://kubernetes.default.svc
# Create a Kustomize app
argocd app create kustomize-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path kustomize-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --kustomize-image gcr.io/heptio-images/ks-guestbook-demo=0.1
argocd app create kustomize-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path kustomize-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --kustomize-image gcr.io/heptio-images/ks-guestbook-demo:0.1
# Create a app using a custom tool:
argocd app create ksane --repo https://github.com/argoproj/argocd-example-apps.git --path plugins/kasane --dest-namespace default --dest-server https://kubernetes.default.svc --config-management-plugin kasane
@@ -151,8 +153,15 @@ func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.
log.Fatalf("app name '%s' does not match app spec metadata.name '%s'", args[0], app.Name)
}
if appName != "" && appName != app.Name {
log.Fatalf("--name argument '%s' does not match app spec metadata.name '%s'", appName, app.Name)
app.Name = appName
}
if app.Name == "" {
log.Fatalf("app.Name is empty. --name argument can be used to provide app.Name")
}
setAppSpecOptions(c.Flags(), &app.Spec, &appOpts)
setParameterOverrides(&app, appOpts.parameters)
setLabels(&app, labels)
} else {
// read arguments
if len(args) == 1 {
@@ -523,6 +532,8 @@ func setAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
setHelmOpt(&spec.Source, helmOpts{values: string(data)})
case "release-name":
setHelmOpt(&spec.Source, helmOpts{releaseName: appOpts.releaseName})
case "helm-version":
setHelmOpt(&spec.Source, helmOpts{version: appOpts.helmVersion})
case "helm-set":
setHelmOpt(&spec.Source, helmOpts{helmSets: appOpts.helmSets})
case "helm-set-string":
@@ -530,7 +541,17 @@ func setAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
case "helm-set-file":
setHelmOpt(&spec.Source, helmOpts{helmSetFiles: appOpts.helmSetFiles})
case "directory-recurse":
spec.Source.Directory = &argoappv1.ApplicationSourceDirectory{Recurse: appOpts.directoryRecurse}
if spec.Source.Directory != nil {
spec.Source.Directory.Recurse = appOpts.directoryRecurse
} else {
spec.Source.Directory = &argoappv1.ApplicationSourceDirectory{Recurse: appOpts.directoryRecurse}
}
case "directory-exclude":
if spec.Source.Directory != nil {
spec.Source.Directory.Exclude = appOpts.directoryExclude
} else {
spec.Source.Directory = &argoappv1.ApplicationSourceDirectory{Exclude: appOpts.directoryExclude}
}
case "config-management-plugin":
spec.Source.Plugin = &argoappv1.ApplicationSourcePlugin{Name: appOpts.configManagementPlugin}
case "dest-name":
@@ -549,6 +570,14 @@ func setAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
setKustomizeOpt(&spec.Source, kustomizeOpts{images: appOpts.kustomizeImages})
case "kustomize-version":
setKustomizeOpt(&spec.Source, kustomizeOpts{version: appOpts.kustomizeVersion})
case "kustomize-common-label":
parsedLabels, err := label.Parse(appOpts.kustomizeCommonLabels)
errors.CheckError(err)
setKustomizeOpt(&spec.Source, kustomizeOpts{commonLabels: parsedLabels})
case "kustomize-common-annotation":
parsedAnnotations, err := label.Parse(appOpts.kustomizeCommonAnnotations)
errors.CheckError(err)
setKustomizeOpt(&spec.Source, kustomizeOpts{commonAnnotations: parsedAnnotations})
case "jsonnet-tla-str":
setJsonnetOpt(&spec.Source, appOpts.jsonnetTlaStr, false)
case "jsonnet-tla-code":
@@ -606,6 +635,12 @@ func setAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
}
spec.SyncPolicy.Automated.SelfHeal = appOpts.selfHeal
}
if flags.Changed("allow-empty") {
if spec.SyncPolicy == nil || spec.SyncPolicy.Automated == nil {
log.Fatal("Cannot set --allow-empty: application not configured with automatic sync")
}
spec.SyncPolicy.Automated.AllowEmpty = appOpts.allowEmpty
}
return visited
}
@@ -623,19 +658,33 @@ func setKsonnetOpt(src *argoappv1.ApplicationSource, env *string) {
}
type kustomizeOpts struct {
namePrefix string
nameSuffix string
images []string
version string
namePrefix string
nameSuffix string
images []string
version string
commonLabels map[string]string
commonAnnotations map[string]string
}
func setKustomizeOpt(src *argoappv1.ApplicationSource, opts kustomizeOpts) {
if src.Kustomize == nil {
src.Kustomize = &argoappv1.ApplicationSourceKustomize{}
}
src.Kustomize.Version = opts.version
src.Kustomize.NamePrefix = opts.namePrefix
src.Kustomize.NameSuffix = opts.nameSuffix
if opts.version != "" {
src.Kustomize.Version = opts.version
}
if opts.namePrefix != "" {
src.Kustomize.NamePrefix = opts.namePrefix
}
if opts.nameSuffix != "" {
src.Kustomize.NameSuffix = opts.nameSuffix
}
if opts.commonLabels != nil {
src.Kustomize.CommonLabels = opts.commonLabels
}
if opts.commonAnnotations != nil {
src.Kustomize.CommonAnnotations = opts.commonAnnotations
}
for _, image := range opts.images {
src.Kustomize.MergeImage(argoappv1.KustomizeImage(image))
}
@@ -648,6 +697,7 @@ type helmOpts struct {
valueFiles []string
values string
releaseName string
version string
helmSets []string
helmSetStrings []string
helmSetFiles []string
@@ -666,6 +716,9 @@ func setHelmOpt(src *argoappv1.ApplicationSource, opts helmOpts) {
if opts.releaseName != "" {
src.Helm.ReleaseName = opts.releaseName
}
if opts.version != "" {
src.Helm.Version = opts.version
}
for _, text := range opts.helmSets {
p, err := argoappv1.NewHelmParameter(text, false)
if err != nil {
@@ -717,39 +770,44 @@ func setJsonnetOptLibs(src *argoappv1.ApplicationSource, libs []string) {
}
type appOptions struct {
repoURL string
appPath string
chart string
env string
revision string
revisionHistoryLimit int
destName string
destServer string
destNamespace string
parameters []string
valuesFiles []string
values string
releaseName string
helmSets []string
helmSetStrings []string
helmSetFiles []string
project string
syncPolicy string
syncOptions []string
autoPrune bool
selfHeal bool
namePrefix string
nameSuffix string
directoryRecurse bool
configManagementPlugin string
jsonnetTlaStr []string
jsonnetTlaCode []string
jsonnetExtVarStr []string
jsonnetExtVarCode []string
jsonnetLibs []string
kustomizeImages []string
kustomizeVersion string
validate bool
repoURL string
appPath string
chart string
env string
revision string
revisionHistoryLimit int
destName string
destServer string
destNamespace string
parameters []string
valuesFiles []string
values string
releaseName string
helmSets []string
helmSetStrings []string
helmSetFiles []string
helmVersion string
project string
syncPolicy string
syncOptions []string
autoPrune bool
selfHeal bool
allowEmpty bool
namePrefix string
nameSuffix string
directoryRecurse bool
configManagementPlugin string
jsonnetTlaStr []string
jsonnetTlaCode []string
jsonnetExtVarStr []string
jsonnetExtVarCode []string
jsonnetLibs []string
kustomizeImages []string
kustomizeVersion string
kustomizeCommonLabels []string
kustomizeCommonAnnotations []string
validate bool
directoryExclude string
}
func addAppFlags(command *cobra.Command, opts *appOptions) {
@@ -766,6 +824,7 @@ func addAppFlags(command *cobra.Command, opts *appOptions) {
command.Flags().StringArrayVar(&opts.valuesFiles, "values", []string{}, "Helm values file(s) to use")
command.Flags().StringVar(&opts.values, "values-literal-file", "", "Filename or URL to import as a literal Helm values block")
command.Flags().StringVar(&opts.releaseName, "release-name", "", "Helm release-name")
command.Flags().StringVar(&opts.helmVersion, "helm-version", "", "Helm version")
command.Flags().StringArrayVar(&opts.helmSets, "helm-set", []string{}, "Helm set values on the command line (can be repeated to set several values: --helm-set key1=val1 --helm-set key2=val2)")
command.Flags().StringArrayVar(&opts.helmSetStrings, "helm-set-string", []string{}, "Helm set STRING values on the command line (can be repeated to set several values: --helm-set-string key1=val1 --helm-set-string key2=val2)")
command.Flags().StringArrayVar(&opts.helmSetFiles, "helm-set-file", []string{}, "Helm set values from respective files specified via the command line (can be repeated to set several values: --helm-set-file key1=path1 --helm-set-file key2=path2)")
@@ -774,6 +833,7 @@ func addAppFlags(command *cobra.Command, opts *appOptions) {
command.Flags().StringArrayVar(&opts.syncOptions, "sync-option", []string{}, "Add or remove a sync options, e.g add `Prune=false`. Remove using `!` prefix, e.g. `!Prune=false`")
command.Flags().BoolVar(&opts.autoPrune, "auto-prune", false, "Set automatic pruning when sync is automated")
command.Flags().BoolVar(&opts.selfHeal, "self-heal", false, "Set self healing when sync is automated")
command.Flags().BoolVar(&opts.allowEmpty, "allow-empty", false, "Set allow zero live resources when sync is automated")
command.Flags().StringVar(&opts.namePrefix, "nameprefix", "", "Kustomize nameprefix")
command.Flags().StringVar(&opts.nameSuffix, "namesuffix", "", "Kustomize namesuffix")
command.Flags().StringVar(&opts.kustomizeVersion, "kustomize-version", "", "Kustomize version")
@@ -786,6 +846,9 @@ func addAppFlags(command *cobra.Command, opts *appOptions) {
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().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().StringVar(&opts.directoryExclude, "directory-exclude", "", "Set glob expression used to exclude files from application source path")
}
// NewApplicationUnsetCommand returns a new instance of an `argocd app unset` command
@@ -994,7 +1057,7 @@ func (p *resourceInfoProvider) IsNamespaced(gk schema.GroupKind) (bool, error) {
return p.namespacedByGk[gk], nil
}
func groupLocalObjs(localObs []*unstructured.Unstructured, liveObjs []*unstructured.Unstructured, appNamespace string) map[kube.ResourceKey]*unstructured.Unstructured {
func groupObjsByKey(localObs []*unstructured.Unstructured, liveObjs []*unstructured.Unstructured, appNamespace string) map[kube.ResourceKey]*unstructured.Unstructured {
namespacedByGk := make(map[schema.GroupKind]bool)
for i := range liveObjs {
if liveObjs[i] != nil {
@@ -1014,12 +1077,19 @@ func groupLocalObjs(localObs []*unstructured.Unstructured, liveObjs []*unstructu
return objByKey
}
type objKeyLiveTarget struct {
key kube.ResourceKey
live *unstructured.Unstructured
target *unstructured.Unstructured
}
// NewApplicationDiffCommand returns a new instance of an `argocd app diff` command
func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
refresh bool
hardRefresh bool
local string
revision string
localRepoRoot string
)
shortDesc := "Perform a diff against the target and live state."
@@ -1043,11 +1113,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
errors.CheckError(err)
liveObjs, err := liveObjects(resources.Items)
errors.CheckError(err)
items := make([]struct {
key kube.ResourceKey
live *unstructured.Unstructured
target *unstructured.Unstructured
}, 0)
items := make([]objKeyLiveTarget, 0)
conn, settingsIf := clientset.NewSettingsClientOrDie()
defer argoio.Close(conn)
@@ -1057,49 +1123,25 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
if local != "" {
conn, clusterIf := clientset.NewClusterClientOrDie()
defer argoio.Close(conn)
cluster, err := clusterIf.Get(context.Background(), &clusterpkg.ClusterQuery{Server: app.Spec.Destination.Server})
cluster, err := clusterIf.Get(context.Background(), &clusterpkg.ClusterQuery{Name: app.Spec.Destination.Name, Server: app.Spec.Destination.Server})
errors.CheckError(err)
localObjs := groupLocalObjs(getLocalObjects(app, local, localRepoRoot, argoSettings.AppLabelKey, cluster.ServerVersion, argoSettings.KustomizeOptions, argoSettings.ConfigManagementPlugins), liveObjs, app.Spec.Destination.Namespace)
for _, res := range resources.Items {
var live = &unstructured.Unstructured{}
err := json.Unmarshal([]byte(res.NormalizedLiveState), &live)
localObjs := groupObjsByKey(getLocalObjects(app, local, localRepoRoot, argoSettings.AppLabelKey, cluster.ServerVersion, argoSettings.KustomizeOptions, argoSettings.ConfigManagementPlugins), liveObjs, app.Spec.Destination.Namespace)
items = groupObjsForDiff(resources, localObjs, items, argoSettings, appName)
} else if revision != "" {
var unstructureds []*unstructured.Unstructured
q := applicationpkg.ApplicationManifestQuery{
Name: &appName,
Revision: revision,
}
res, err := appIf.GetManifests(context.Background(), &q)
errors.CheckError(err)
for _, mfst := range res.Manifests {
obj, err := argoappv1.UnmarshalToUnstructured(mfst)
errors.CheckError(err)
key := kube.ResourceKey{Name: res.Name, Namespace: res.Namespace, Group: res.Group, Kind: res.Kind}
if key.Kind == kube.SecretKind && key.Group == "" {
// Don't bother comparing secrets, argo-cd doesn't have access to k8s secret data
delete(localObjs, key)
continue
}
if local, ok := localObjs[key]; ok || live != nil {
if local != nil && !kube.IsCRD(local) {
err = argokube.SetAppInstanceLabel(local, argoSettings.AppLabelKey, appName)
errors.CheckError(err)
}
items = append(items, struct {
key kube.ResourceKey
live *unstructured.Unstructured
target *unstructured.Unstructured
}{
live: live,
target: local,
key: key,
})
delete(localObjs, key)
}
}
for key, local := range localObjs {
items = append(items, struct {
key kube.ResourceKey
live *unstructured.Unstructured
target *unstructured.Unstructured
}{
live: nil,
target: local,
key: key,
})
unstructureds = append(unstructureds, obj)
}
groupedObjs := groupObjsByKey(unstructureds, liveObjs, app.Spec.Destination.Namespace)
items = groupObjsForDiff(resources, groupedObjs, items, argoSettings, appName)
} else {
for i := range resources.Items {
res := resources.Items[i]
@@ -1111,15 +1153,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
err = json.Unmarshal([]byte(res.TargetState), &target)
errors.CheckError(err)
items = append(items, struct {
key kube.ResourceKey
live *unstructured.Unstructured
target *unstructured.Unstructured
}{
live: live,
target: target,
key: kube.NewResourceKey(res.Group, res.Kind, res.Namespace, res.Name),
})
items = append(items, objKeyLiveTarget{kube.NewResourceKey(res.Group, res.Kind, res.Namespace, res.Name), live, target})
}
}
@@ -1136,7 +1170,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
normalizer, err := argo.NewDiffNormalizer(app.Spec.IgnoreDifferences, overrides)
errors.CheckError(err)
diffRes, err := diff.Diff(item.target, item.live, normalizer, diff.GetDefaultDiffOptions())
diffRes, err := diff.Diff(item.target, item.live, diff.WithNormalizer(normalizer))
errors.CheckError(err)
if diffRes.Modified || item.target == nil || item.live == nil {
@@ -1154,7 +1188,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
}
foundDiffs = true
_ = diff.PrintDiff(item.key.Name, live, target)
_ = cli.PrintDiff(item.key.Name, live, target)
}
}
if foundDiffs {
@@ -1166,10 +1200,44 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
command.Flags().BoolVar(&refresh, "refresh", false, "Refresh application data when retrieving")
command.Flags().BoolVar(&hardRefresh, "hard-refresh", false, "Refresh application data as well as target manifests cache")
command.Flags().StringVar(&local, "local", "", "Compare live app to a local manifests")
command.Flags().StringVar(&revision, "revision", "", "Compare live app to a particular revision")
command.Flags().StringVar(&localRepoRoot, "local-repo-root", "/", "Path to the repository root. Used together with --local allows setting the repository root")
return command
}
func groupObjsForDiff(resources *application.ManagedResourcesResponse, objs map[kube.ResourceKey]*unstructured.Unstructured, items []objKeyLiveTarget, argoSettings *settings.Settings, appName string) []objKeyLiveTarget {
for _, res := range resources.Items {
var live = &unstructured.Unstructured{}
err := json.Unmarshal([]byte(res.NormalizedLiveState), &live)
errors.CheckError(err)
key := kube.ResourceKey{Name: res.Name, Namespace: res.Namespace, Group: res.Group, Kind: res.Kind}
if key.Kind == kube.SecretKind && key.Group == "" {
// Don't bother comparing secrets, argo-cd doesn't have access to k8s secret data
delete(objs, key)
continue
}
if local, ok := objs[key]; ok || live != nil {
if local != nil && !kube.IsCRD(local) {
err = argokube.SetAppInstanceLabel(local, argoSettings.AppLabelKey, appName)
errors.CheckError(err)
}
items = append(items, objKeyLiveTarget{key, live, local})
delete(objs, key)
}
}
for key, local := range objs {
if key.Kind == kube.SecretKind && key.Group == "" {
// Don't bother comparing secrets, argo-cd doesn't have access to k8s secret data
delete(objs, key)
continue
}
items = append(items, objKeyLiveTarget{key, nil, local})
}
return items
}
// NewApplicationDeleteCommand returns a new instance of an `argocd app delete` command
func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
@@ -1541,7 +1609,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
conn, clusterIf := acdClient.NewClusterClientOrDie()
defer argoio.Close(conn)
cluster, err := clusterIf.Get(context.Background(), &clusterpkg.ClusterQuery{Server: app.Spec.Destination.Server})
cluster, err := clusterIf.Get(context.Background(), &clusterpkg.ClusterQuery{Name: app.Spec.Destination.Name, Server: app.Spec.Destination.Server})
errors.CheckError(err)
argoio.Close(conn)
localObjsStrings = getLocalObjectsString(app, local, localRepoRoot, argoSettings.AppLabelKey, cluster.ServerVersion, argoSettings.KustomizeOptions, argoSettings.ConfigManagementPlugins)
@@ -1833,7 +1901,7 @@ func waitOnApplicationStatus(acdClient apiclient.Client, appName string, timeout
selectedResourcesAreReady = checkResourceStatus(watchSync, watchHealth, watchOperation, watchSuspended, string(app.Status.Health.Status), string(app.Status.Sync.Status), appEvent.Application.Operation)
}
if selectedResourcesAreReady && !operationInProgress {
if selectedResourcesAreReady && (!operationInProgress || !watchOperation) {
app = printFinalStatus(app)
return app, nil
}
@@ -2284,7 +2352,7 @@ func filterResources(command *cobra.Command, resources []*argoappv1.ResourceDiff
if resourceName != "" && resourceName != obj.GetName() {
continue
}
if kind != gvk.Kind {
if kind != "" && kind != gvk.Kind {
continue
}
deepCopy := obj.DeepCopy()

View File

@@ -8,14 +8,14 @@ import (
"strconv"
"text/tabwriter"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
"github.com/argoproj/gitops-engine/pkg/utils/io"
"github.com/ghodss/yaml"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
applicationpkg "github.com/argoproj/argo-cd/pkg/apiclient/application"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/io"
)
type DisplayedAction struct {
@@ -100,7 +100,6 @@ func NewApplicationResourceActionsListCommand(clientOpts *argocdclient.ClientOpt
case "":
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintf(w, "GROUP\tKIND\tNAME\tACTION\tDISABLED\n")
fmt.Println()
for _, action := range availableActions {
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", action.Group, action.Kind, action.Name, action.Action, strconv.FormatBool(action.Disabled))
}

View File

@@ -40,6 +40,49 @@ func Test_setHelmOpt(t *testing.T) {
setHelmOpt(&src, helmOpts{helmSetFiles: []string{"foo=bar"}})
assert.Equal(t, []v1alpha1.HelmFileParameter{{Name: "foo", Path: "bar"}}, src.Helm.FileParameters)
})
t.Run("Version", func(t *testing.T) {
src := v1alpha1.ApplicationSource{}
setHelmOpt(&src, helmOpts{version: "v3"})
assert.Equal(t, "v3", src.Helm.Version)
})
}
func Test_setKustomizeOpt(t *testing.T) {
t.Run("No kustomize", func(t *testing.T) {
src := v1alpha1.ApplicationSource{}
setKustomizeOpt(&src, kustomizeOpts{})
assert.Nil(t, src.Kustomize)
})
t.Run("Name prefix", func(t *testing.T) {
src := v1alpha1.ApplicationSource{}
setKustomizeOpt(&src, kustomizeOpts{namePrefix: "test-"})
assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{NamePrefix: "test-"}, src.Kustomize)
})
t.Run("Name suffix", func(t *testing.T) {
src := v1alpha1.ApplicationSource{}
setKustomizeOpt(&src, kustomizeOpts{nameSuffix: "-test"})
assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{NameSuffix: "-test"}, src.Kustomize)
})
t.Run("Images", func(t *testing.T) {
src := v1alpha1.ApplicationSource{}
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("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("Common labels", func(t *testing.T) {
src := v1alpha1.ApplicationSource{}
setKustomizeOpt(&src, kustomizeOpts{commonLabels: map[string]string{"foo1": "bar1", "foo2": "bar2"}})
assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{CommonLabels: map[string]string{"foo1": "bar1", "foo2": "bar2"}}, src.Kustomize)
})
t.Run("Common annotations", func(t *testing.T) {
src := v1alpha1.ApplicationSource{}
setKustomizeOpt(&src, kustomizeOpts{commonAnnotations: map[string]string{"foo1": "bar1", "foo2": "bar2"}})
assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{CommonAnnotations: map[string]string{"foo1": "bar1", "foo2": "bar2"}}, src.Kustomize)
})
}
func Test_setJsonnetOpt(t *testing.T) {

View File

@@ -9,14 +9,14 @@ import (
"strings"
"text/tabwriter"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
"github.com/argoproj/gitops-engine/pkg/utils/io"
"github.com/spf13/cobra"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
certificatepkg "github.com/argoproj/argo-cd/pkg/apiclient/certificate"
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
certutil "github.com/argoproj/argo-cd/util/cert"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/io"
)
// NewCertCommand returns a new instance of an `argocd repo` command

View File

@@ -9,8 +9,6 @@ import (
"strings"
"text/tabwriter"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
"github.com/argoproj/gitops-engine/pkg/utils/io"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"k8s.io/client-go/kubernetes"
@@ -22,6 +20,8 @@ import (
clusterpkg "github.com/argoproj/argo-cd/pkg/apiclient/cluster"
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/clusterauth"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/io"
)
// NewClusterCommand returns a new instance of an `argocd cluster` command
@@ -58,14 +58,20 @@ func NewClusterCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clientc
// NewClusterAddCommand returns a new instance of an `argocd cluster add` command
func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clientcmd.PathOptions) *cobra.Command {
var (
inCluster bool
upsert bool
serviceAccount string
awsRoleArn string
awsClusterName string
systemNamespace string
namespaces []string
name string
inCluster bool
upsert bool
serviceAccount string
awsRoleArn string
awsClusterName string
systemNamespace string
namespaces []string
name string
shard int64
execProviderCommand string
execProviderArgs []string
execProviderEnv map[string]string
execProviderAPIVersion string
execProviderInstallHint string
)
var command = &cobra.Command{
Use: "add CONTEXT",
@@ -94,11 +100,20 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
managerBearerToken := ""
var awsAuthConf *argoappv1.AWSAuthConfig
var execProviderConf *argoappv1.ExecProviderConfig
if awsClusterName != "" {
awsAuthConf = &argoappv1.AWSAuthConfig{
ClusterName: awsClusterName,
RoleARN: awsRoleArn,
}
} else if execProviderCommand != "" {
execProviderConf = &argoappv1.ExecProviderConfig{
Command: execProviderCommand,
Args: execProviderArgs,
Env: execProviderEnv,
APIVersion: execProviderAPIVersion,
InstallHint: execProviderInstallHint,
}
} else {
// Install RBAC resources for managing the cluster
clientset, err := kubernetes.NewForConfig(conf)
@@ -115,10 +130,13 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
if name != "" {
contextName = name
}
clst := newCluster(contextName, namespaces, conf, managerBearerToken, awsAuthConf)
clst := newCluster(contextName, namespaces, conf, managerBearerToken, awsAuthConf, execProviderConf)
if inCluster {
clst.Server = common.KubernetesInternalAPIServerAddr
}
if shard >= 0 {
clst.Shard = &shard
}
clstCreateReq := clusterpkg.ClusterCreateRequest{
Cluster: clst,
Upsert: upsert,
@@ -137,6 +155,12 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
command.Flags().StringVar(&systemNamespace, "system-namespace", common.DefaultSystemNamespace, "Use different system namespace")
command.Flags().StringArrayVar(&namespaces, "namespace", nil, "List of namespaces which are allowed to manage")
command.Flags().StringVar(&name, "name", "", "Overwrite the cluster name")
command.Flags().Int64Var(&shard, "shard", -1, "Cluster shard number; inferred from hostname if not set")
command.Flags().StringVar(&execProviderCommand, "exec-command", "", "Command to run to provide client credentials to the cluster. You may need to build a custom ArgoCD image to ensure the command is available at runtime.")
command.Flags().StringArrayVar(&execProviderArgs, "exec-command-args", nil, "Arguments to supply to the --exec-command command")
command.Flags().StringToStringVar(&execProviderEnv, "exec-command-env", nil, "Environment vars to set when running the --exec-command command")
command.Flags().StringVar(&execProviderAPIVersion, "exec-command-api-version", "", "Preferred input version of the ExecInfo for the --exec-command")
command.Flags().StringVar(&execProviderInstallHint, "exec-command-install-hint", "", "Text shown to the user when the --exec-command executable doesn't seem to be present")
return command
}
@@ -179,7 +203,7 @@ func printKubeContexts(ca clientcmd.ConfigAccess) {
}
}
func newCluster(name string, namespaces []string, conf *rest.Config, managerBearerToken string, awsAuthConf *argoappv1.AWSAuthConfig) *argoappv1.Cluster {
func newCluster(name string, namespaces []string, conf *rest.Config, managerBearerToken string, awsAuthConf *argoappv1.AWSAuthConfig, execProviderConf *argoappv1.ExecProviderConfig) *argoappv1.Cluster {
tlsClientConfig := argoappv1.TLSClientConfig{
Insecure: conf.TLSClientConfig.Insecure,
ServerName: conf.TLSClientConfig.ServerName,
@@ -208,8 +232,9 @@ func newCluster(name string, namespaces []string, conf *rest.Config, managerBear
Name: name,
Namespaces: namespaces,
Config: argoappv1.ClusterConfig{
TLSClientConfig: tlsClientConfig,
AWSAuthConfig: awsAuthConf,
TLSClientConfig: tlsClientConfig,
AWSAuthConfig: awsAuthConf,
ExecProviderConfig: execProviderConf,
},
}

View File

@@ -45,7 +45,8 @@ func Test_newCluster(t *testing.T) {
Host: "test-endpoint.example.com",
},
"test-bearer-token",
&v1alpha1.AWSAuthConfig{})
&v1alpha1.AWSAuthConfig{},
&v1alpha1.ExecProviderConfig{})
assert.Equal(t, "test-cert-data", string(clusterWithData.Config.CertData))
assert.Equal(t, "test-key-data", string(clusterWithData.Config.KeyData))
@@ -62,7 +63,8 @@ func Test_newCluster(t *testing.T) {
Host: "test-endpoint.example.com",
},
"test-bearer-token",
&v1alpha1.AWSAuthConfig{})
&v1alpha1.AWSAuthConfig{},
&v1alpha1.ExecProviderConfig{})
assert.True(t, strings.Contains(string(clusterWithFiles.Config.CertData), "test-cert-data"))
assert.True(t, strings.Contains(string(clusterWithFiles.Config.KeyData), "test-key-data"))
@@ -77,7 +79,8 @@ func Test_newCluster(t *testing.T) {
Host: "test-endpoint.example.com",
},
"test-bearer-token",
&v1alpha1.AWSAuthConfig{})
&v1alpha1.AWSAuthConfig{},
&v1alpha1.ExecProviderConfig{})
assert.Equal(t, "test-bearer-token", clusterWithBearerToken.Config.BearerToken)
}

View File

@@ -8,11 +8,11 @@ import (
"strings"
"text/tabwriter"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/localconfig"
)

View File

@@ -8,13 +8,13 @@ import (
"strings"
"text/tabwriter"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
argoio "github.com/argoproj/gitops-engine/pkg/utils/io"
"github.com/spf13/cobra"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
gpgkeypkg "github.com/argoproj/argo-cd/pkg/apiclient/gpgkey"
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/errors"
argoio "github.com/argoproj/argo-cd/util/io"
)
// NewGPGCommand returns a new instance of an `argocd repo` command

View File

@@ -2,17 +2,18 @@ package commands
import (
"context"
"crypto/sha256"
"encoding/base64"
"fmt"
"html"
"net/http"
"os"
"strconv"
"strings"
"time"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
"github.com/argoproj/gitops-engine/pkg/utils/io"
"github.com/coreos/go-oidc"
"github.com/dgrijalva/jwt-go"
"github.com/dgrijalva/jwt-go/v4"
log "github.com/sirupsen/logrus"
"github.com/skratchdot/open-golang/open"
"github.com/spf13/cobra"
@@ -22,7 +23,10 @@ import (
sessionpkg "github.com/argoproj/argo-cd/pkg/apiclient/session"
settingspkg "github.com/argoproj/argo-cd/pkg/apiclient/settings"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/errors"
grpc_util "github.com/argoproj/argo-cd/util/grpc"
"github.com/argoproj/argo-cd/util/io"
jwtutil "github.com/argoproj/argo-cd/util/jwt"
"github.com/argoproj/argo-cd/util/localconfig"
oidcutil "github.com/argoproj/argo-cd/util/oidc"
"github.com/argoproj/argo-cd/util/rand"
@@ -111,7 +115,7 @@ func NewLoginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comman
}
parser := &jwt.Parser{
SkipClaimsValidation: true,
ValidationHelper: jwt.NewValidationHelper(jwt.WithoutClaimsValidation(), jwt.WithoutAudienceValidation()),
}
claims := jwt.MapClaims{}
_, _, err := parser.ParseUnverified(tokenString, &claims)
@@ -159,13 +163,13 @@ func NewLoginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comman
}
func userDisplayName(claims jwt.MapClaims) string {
if email, ok := claims["email"]; ok && email != nil {
return email.(string)
if email := jwtutil.StringField(claims, "email"); email != "" {
return email
}
if name, ok := claims["name"]; ok && name != nil {
return name.(string)
if name := jwtutil.StringField(claims, "name"); name != "" {
return name
}
return claims["sub"].(string)
return jwtutil.StringField(claims, "sub")
}
// oauth2Login opens a browser, runs a temporary HTTP server to delegate OAuth2 login flow and
@@ -188,17 +192,22 @@ func oauth2Login(ctx context.Context, port int, oidcSettings *settingspkg.OIDCCo
var refreshToken string
handleErr := func(w http.ResponseWriter, errMsg string) {
http.Error(w, errMsg, http.StatusBadRequest)
http.Error(w, html.EscapeString(errMsg), http.StatusBadRequest)
completionChan <- errMsg
}
// PKCE implementation of https://tools.ietf.org/html/rfc7636
codeVerifier := rand.RandStringCharset(43, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~")
codeChallengeHash := sha256.Sum256([]byte(codeVerifier))
codeChallenge := base64.RawURLEncoding.EncodeToString(codeChallengeHash[:])
// Authorization redirect callback from OAuth2 auth flow.
// Handles both implicit and authorization code flow
callbackHandler := func(w http.ResponseWriter, r *http.Request) {
log.Debugf("Callback: %s", r.URL)
if formErr := r.FormValue("error"); formErr != "" {
handleErr(w, formErr+": "+r.FormValue("error_description"))
handleErr(w, fmt.Sprintf("%s: %s", formErr, r.FormValue("error_description")))
return
}
@@ -231,7 +240,8 @@ func oauth2Login(ctx context.Context, port int, oidcSettings *settingspkg.OIDCCo
handleErr(w, fmt.Sprintf("no code in request: %q", r.Form))
return
}
tok, err := oauth2conf.Exchange(ctx, code)
opts := []oauth2.AuthCodeOption{oauth2.SetAuthURLParam("code_verifier", codeVerifier)}
tok, err := oauth2conf.Exchange(ctx, code, opts...)
if err != nil {
handleErr(w, err.Error())
return
@@ -267,6 +277,8 @@ func oauth2Login(ctx context.Context, port int, oidcSettings *settingspkg.OIDCCo
switch grantType {
case oidcutil.GrantTypeAuthorizationCode:
opts = append(opts, oauth2.SetAuthURLParam("code_challenge", codeChallenge))
opts = append(opts, oauth2.SetAuthURLParam("code_challenge_method", "S256"))
url = oauth2conf.AuthCodeURL(stateNonce, opts...)
case oidcutil.GrantTypeImplicit:
url = oidcutil.ImplicitFlowURL(oauth2conf, stateNonce, opts...)

View File

@@ -0,0 +1,31 @@
package commands
import (
"testing"
"github.com/dgrijalva/jwt-go/v4"
"github.com/stretchr/testify/assert"
)
//
func Test_userDisplayName_email(t *testing.T) {
claims := jwt.MapClaims{"iss": "qux", "sub": "foo", "email": "firstname.lastname@example.com", "groups": []string{"baz"}}
actualName := userDisplayName(claims)
expectedName := "firstname.lastname@example.com"
assert.Equal(t, expectedName, actualName)
}
func Test_userDisplayName_name(t *testing.T) {
claims := jwt.MapClaims{"iss": "qux", "sub": "foo", "name": "Firstname Lastname", "groups": []string{"baz"}}
actualName := userDisplayName(claims)
expectedName := "Firstname Lastname"
assert.Equal(t, expectedName, actualName)
}
func Test_userDisplayName_sub(t *testing.T) {
claims := jwt.MapClaims{"iss": "qux", "sub": "foo", "groups": []string{"baz"}}
actualName := userDisplayName(claims)
expectedName := "foo"
assert.Equal(t, expectedName, actualName)
}

View File

@@ -4,11 +4,11 @@ import (
"fmt"
"os"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/localconfig"
)

View File

@@ -12,8 +12,6 @@ import (
"text/tabwriter"
"time"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
argoio "github.com/argoproj/gitops-engine/pkg/utils/io"
humanize "github.com/dustin/go-humanize"
"github.com/ghodss/yaml"
log "github.com/sirupsen/logrus"
@@ -28,8 +26,10 @@ import (
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/config"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/git"
"github.com/argoproj/argo-cd/util/gpg"
argoio "github.com/argoproj/argo-cd/util/io"
)
type projectOpts struct {
@@ -564,9 +564,9 @@ func modifyResourceListCmd(cmdUse, cmdDesc string, clientOpts *argocdclient.Clie
defaultList string
)
if namespacedList {
defaultList = "black"
defaultList = "deny"
} else {
defaultList = "white"
defaultList = "allow"
}
var command = &cobra.Command{
Use: cmdUse,
@@ -582,24 +582,24 @@ func modifyResourceListCmd(cmdUse, cmdDesc string, clientOpts *argocdclient.Clie
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
errors.CheckError(err)
var list, white, black *[]metav1.GroupKind
var list, allowList, denyList *[]metav1.GroupKind
var listAction, listDesc string
var add bool
if namespacedList {
white, black = &proj.Spec.NamespaceResourceWhitelist, &proj.Spec.NamespaceResourceBlacklist
allowList, denyList = &proj.Spec.NamespaceResourceWhitelist, &proj.Spec.NamespaceResourceBlacklist
listDesc = "namespaced"
} else {
white, black = &proj.Spec.ClusterResourceWhitelist, &proj.Spec.ClusterResourceBlacklist
allowList, denyList = &proj.Spec.ClusterResourceWhitelist, &proj.Spec.ClusterResourceBlacklist
listDesc = "cluster"
}
if listType == "white" {
list = white
listAction = "whitelisted"
if (listType == "allow") || (listType == "white") {
list = allowList
listAction = "allowed"
add = allow
} else {
list = black
listAction = "blacklisted"
list = denyList
listAction = "denied"
add = !allow
}
@@ -609,35 +609,35 @@ func modifyResourceListCmd(cmdUse, cmdDesc string, clientOpts *argocdclient.Clie
}
},
}
command.Flags().StringVarP(&listType, "list", "l", defaultList, "Use blacklist or whitelist. This can only be 'white' or 'black'")
command.Flags().StringVarP(&listType, "list", "l", defaultList, "Use deny list or allow list. This can only be 'allow' or 'deny'")
return command
}
// NewProjectAllowNamespaceResourceCommand returns a new instance of an `deny-cluster-resources` command
func NewProjectAllowNamespaceResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
use := "allow-namespace-resource PROJECT GROUP KIND"
desc := "Removes a namespaced API resource from the blacklist or add a namespaced API resource to the whitelist"
desc := "Removes a namespaced API resource from the deny list or add a namespaced API resource to the allow list"
return modifyResourceListCmd(use, desc, clientOpts, true, true)
}
// NewProjectDenyNamespaceResourceCommand returns a new instance of an `argocd proj deny-namespace-resource` command
func NewProjectDenyNamespaceResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
use := "deny-namespace-resource PROJECT GROUP KIND"
desc := "Adds a namespaced API resource to the blacklist or removes a namespaced API resource from the whitelist"
desc := "Adds a namespaced API resource to the deny list or removes a namespaced API resource from the allow list"
return modifyResourceListCmd(use, desc, clientOpts, false, true)
}
// NewProjectDenyClusterResourceCommand returns a new instance of an `deny-cluster-resource` command
func NewProjectDenyClusterResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
use := "deny-cluster-resource PROJECT GROUP KIND"
desc := "Removes a cluster-scoped API resource from the whitelist and adds it to blacklist"
desc := "Removes a cluster-scoped API resource from the allow list and adds it to deny list"
return modifyResourceListCmd(use, desc, clientOpts, false, false)
}
// NewProjectAllowClusterResourceCommand returns a new instance of an `argocd proj allow-cluster-resource` command
func NewProjectAllowClusterResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
use := "allow-cluster-resource PROJECT GROUP KIND"
desc := "Adds a cluster-scoped API resource to the whitelist and removes it from blacklist"
desc := "Adds a cluster-scoped API resource to the allow list and removes it from deny list"
return modifyResourceListCmd(use, desc, clientOpts, true, false)
}
@@ -800,7 +800,7 @@ func printProjectLine(w io.Writer, p *v1alpha1.AppProject) {
}
func printProject(p *v1alpha1.AppProject) {
const printProjFmtStr = "%-34s%s\n"
const printProjFmtStr = "%-29s%s\n"
fmt.Printf(printProjFmtStr, "Name:", p.Name)
fmt.Printf(printProjFmtStr, "Description:", p.Spec.Description)
@@ -825,22 +825,22 @@ func printProject(p *v1alpha1.AppProject) {
fmt.Printf(printProjFmtStr, "", p.Spec.SourceRepos[i])
}
// Print whitelisted cluster resources
// Print allowed cluster resources
cwl0 := "<none>"
if len(p.Spec.ClusterResourceWhitelist) > 0 {
cwl0 = fmt.Sprintf("%s/%s", p.Spec.ClusterResourceWhitelist[0].Group, p.Spec.ClusterResourceWhitelist[0].Kind)
}
fmt.Printf(printProjFmtStr, "Whitelisted Cluster Resources:", cwl0)
fmt.Printf(printProjFmtStr, "Allowed Cluster Resources:", cwl0)
for i := 1; i < len(p.Spec.ClusterResourceWhitelist); i++ {
fmt.Printf(printProjFmtStr, "", fmt.Sprintf("%s/%s", p.Spec.ClusterResourceWhitelist[i].Group, p.Spec.ClusterResourceWhitelist[i].Kind))
}
// Print blacklisted namespaced resources
// Print denied namespaced resources
rbl0 := "<none>"
if len(p.Spec.NamespaceResourceBlacklist) > 0 {
rbl0 = fmt.Sprintf("%s/%s", p.Spec.NamespaceResourceBlacklist[0].Group, p.Spec.NamespaceResourceBlacklist[0].Kind)
}
fmt.Printf(printProjFmtStr, "Blacklisted Namespaced Resources:", rbl0)
fmt.Printf(printProjFmtStr, "Denied Namespaced Resources:", rbl0)
for i := 1; i < len(p.Spec.NamespaceResourceBlacklist); i++ {
fmt.Printf(printProjFmtStr, "", fmt.Sprintf("%s/%s", p.Spec.NamespaceResourceBlacklist[i].Group, p.Spec.NamespaceResourceBlacklist[i].Kind))
}

View File

@@ -6,15 +6,18 @@ import (
"os"
"strconv"
"text/tabwriter"
"time"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
"github.com/argoproj/gitops-engine/pkg/utils/io"
timeutil "github.com/argoproj/pkg/time"
jwtgo "github.com/dgrijalva/jwt-go/v4"
"github.com/spf13/cobra"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
projectpkg "github.com/argoproj/argo-cd/pkg/apiclient/project"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/io"
"github.com/argoproj/argo-cd/util/jwt"
)
const (
@@ -36,6 +39,7 @@ func NewProjectRoleCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman
roleCommand.AddCommand(NewProjectRoleCreateCommand(clientOpts))
roleCommand.AddCommand(NewProjectRoleDeleteCommand(clientOpts))
roleCommand.AddCommand(NewProjectRoleCreateTokenCommand(clientOpts))
roleCommand.AddCommand(NewProjectRoleListTokensCommand(clientOpts))
roleCommand.AddCommand(NewProjectRoleDeleteTokenCommand(clientOpts))
roleCommand.AddCommand(NewProjectRoleAddPolicyCommand(clientOpts))
roleCommand.AddCommand(NewProjectRoleRemovePolicyCommand(clientOpts))
@@ -195,14 +199,25 @@ func NewProjectRoleDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.
return command
}
func tokenTimeToString(t int64) string {
tokenTimeToString := "Never"
if t > 0 {
tokenTimeToString = time.Unix(t, 0).Format(time.RFC3339)
}
return tokenTimeToString
}
// NewProjectRoleCreateTokenCommand returns a new instance of an `argocd proj role create-token` command
func NewProjectRoleCreateTokenCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
expiresIn string
expiresIn string
outputTokenOnly bool
tokenID string
)
var command = &cobra.Command{
Use: "create-token PROJECT ROLE-NAME",
Short: "Create a project token",
Use: "create-token PROJECT ROLE-NAME",
Short: "Create a project token",
Aliases: []string{"token-create"},
Run: func(c *cobra.Command, args []string) {
if len(args) != 2 {
c.HelpFunc()(c, args)
@@ -212,23 +227,109 @@ func NewProjectRoleCreateTokenCommand(clientOpts *argocdclient.ClientOptions) *c
roleName := args[1]
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
defer io.Close(conn)
if expiresIn == "" {
expiresIn = "0s"
}
duration, err := timeutil.ParseDuration(expiresIn)
errors.CheckError(err)
token, err := projIf.CreateToken(context.Background(), &projectpkg.ProjectTokenCreateRequest{Project: projName, Role: roleName, ExpiresIn: int64(duration.Seconds())})
tokenResponse, err := projIf.CreateToken(context.Background(), &projectpkg.ProjectTokenCreateRequest{
Project: projName,
Role: roleName,
ExpiresIn: int64(duration.Seconds()),
Id: tokenID,
})
errors.CheckError(err)
fmt.Println(token.Token)
token, err := jwtgo.Parse(tokenResponse.Token, nil)
if token == nil {
err = fmt.Errorf("received malformed token %v", err)
errors.CheckError(err)
return
}
claims := token.Claims.(jwtgo.MapClaims)
issuedAt, _ := jwt.IssuedAt(claims)
expiresAt := int64(jwt.Float64Field(claims, "exp"))
id := jwt.StringField(claims, "jti")
subject := jwt.StringField(claims, "sub")
if !outputTokenOnly {
fmt.Printf("Create token succeeded for %s.\n", subject)
fmt.Printf(" ID: %s\n Issued At: %s\n Expires At: %s\n",
id, tokenTimeToString(issuedAt), tokenTimeToString(expiresAt),
)
fmt.Println(" Token: " + tokenResponse.Token)
} else {
fmt.Println(tokenResponse.Token)
}
},
}
command.Flags().StringVarP(&expiresIn, "expires-in", "e", "0s", "Duration before the token will expire. (Default: No expiration)")
command.Flags().StringVarP(&expiresIn, "expires-in", "e", "",
"Duration before the token will expire, eg \"12h\", \"7d\". (Default: No expiration)",
)
command.Flags().StringVarP(&tokenID, "id", "i", "", "Token unique identifier. (Default: Random UUID)")
command.Flags().BoolVarP(&outputTokenOnly, "token-only", "t", false, "Output token only - for use in scripts.")
return command
}
func NewProjectRoleListTokensCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
useUnixTime bool
)
var command = &cobra.Command{
Use: "list-tokens PROJECT ROLE-NAME",
Short: "List tokens for a given role.",
Aliases: []string{"list-token", "token-list"},
Run: func(c *cobra.Command, args []string) {
if len(args) != 2 {
c.HelpFunc()(c, args)
os.Exit(1)
}
projName := args[0]
roleName := args[1]
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
defer io.Close(conn)
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
errors.CheckError(err)
role, _, err := proj.GetRoleByName(roleName)
errors.CheckError(err)
if len(role.JWTTokens) == 0 {
fmt.Printf("No tokens for %s.%s\n", projName, roleName)
return
}
writer := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
_, err = fmt.Fprintf(writer, "ID\tISSUED AT\tEXPIRES AT\n")
errors.CheckError(err)
tokenRowFormat := "%s\t%v\t%v\n"
for _, token := range role.JWTTokens {
if useUnixTime {
_, _ = fmt.Fprintf(writer, tokenRowFormat, token.ID, token.IssuedAt, token.ExpiresAt)
} else {
_, _ = fmt.Fprintf(writer, tokenRowFormat, token.ID, tokenTimeToString(token.IssuedAt), tokenTimeToString(token.ExpiresAt))
}
}
err = writer.Flush()
errors.CheckError(err)
},
}
command.Flags().BoolVarP(&useUnixTime, "unixtime", "u", false,
"Print timestamps as Unix time instead of converting. Useful for piping into delete-token.",
)
return command
}
// NewProjectRoleDeleteTokenCommand returns a new instance of an `argocd proj role delete-token` command
func NewProjectRoleDeleteTokenCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var command = &cobra.Command{
Use: "delete-token PROJECT ROLE-NAME ISSUED-AT",
Short: "Delete a project token",
Use: "delete-token PROJECT ROLE-NAME ISSUED-AT",
Short: "Delete a project token",
Aliases: []string{"token-delete", "remove-token"},
Run: func(c *cobra.Command, args []string) {
if len(args) != 3 {
c.HelpFunc()(c, args)

View File

@@ -8,13 +8,13 @@ import (
"strings"
"text/tabwriter"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
"github.com/argoproj/gitops-engine/pkg/utils/io"
"github.com/spf13/cobra"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
projectpkg "github.com/argoproj/argo-cd/pkg/apiclient/project"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/io"
)
// NewProjectWindowsCommand returns a new instance of the `argocd proj windows` command

View File

@@ -5,14 +5,14 @@ import (
"fmt"
"os"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
argoio "github.com/argoproj/gitops-engine/pkg/utils/io"
"github.com/coreos/go-oidc"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
settingspkg "github.com/argoproj/argo-cd/pkg/apiclient/settings"
"github.com/argoproj/argo-cd/util/errors"
argoio "github.com/argoproj/argo-cd/util/io"
"github.com/argoproj/argo-cd/util/localconfig"
"github.com/argoproj/argo-cd/util/session"
)

View File

@@ -7,8 +7,6 @@ import (
"os"
"text/tabwriter"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
"github.com/argoproj/gitops-engine/pkg/utils/io"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -17,7 +15,9 @@ import (
repositorypkg "github.com/argoproj/argo-cd/pkg/apiclient/repository"
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/git"
"github.com/argoproj/argo-cd/util/io"
)
// NewRepoCommand returns a new instance of an `argocd repo` command
@@ -49,6 +49,7 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
tlsClientCertPath string
tlsClientCertKeyPath string
enableLfs bool
enableOci bool
)
// For better readability and easier formatting
@@ -69,6 +70,9 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
# Add a private Helm repository named 'stable' via HTTPS
argocd repo add https://kubernetes-charts.storage.googleapis.com --type helm --name stable --username test --password test
# Add a private Helm OCI-based repository named 'stable' via HTTPS
argocd repo add helm-oci-registry.cn-zhangjiakou.cr.aliyuncs.com --type helm --name stable --enable-oci --username test --password test
`
var command = &cobra.Command{
@@ -126,6 +130,7 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
repo.InsecureIgnoreHostKey = insecureIgnoreHostKey
repo.Insecure = insecureSkipServerVerification
repo.EnableLFS = enableLfs
repo.EnableOCI = enableOci
if repo.Type == "helm" && repo.Name == "" {
errors.CheckError(fmt.Errorf("Must specify --name for repos of type 'helm'"))
@@ -157,6 +162,7 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
TlsClientCertData: repo.TLSClientCertData,
TlsClientCertKey: repo.TLSClientCertKey,
Insecure: repo.IsInsecure(),
EnableOci: repo.EnableOCI,
}
_, err := repoIf.ValidateAccess(context.Background(), &repoAccessReq)
errors.CheckError(err)
@@ -181,6 +187,7 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
command.Flags().BoolVar(&insecureIgnoreHostKey, "insecure-ignore-host-key", false, "disables SSH strict host key checking (deprecated, use --insecure-skip-server-verification instead)")
command.Flags().BoolVar(&insecureSkipServerVerification, "insecure-skip-server-verification", false, "disables server certificate and host key checks")
command.Flags().BoolVar(&enableLfs, "enable-lfs", false, "enable git-lfs (Large File Support) on this repository")
command.Flags().BoolVar(&enableOci, "enable-oci", false, "enable helm-oci (Helm OCI-Based Repository)")
command.Flags().BoolVar(&upsert, "upsert", false, "Override an existing repository with the same name even if the spec differs")
return command
}
@@ -209,7 +216,7 @@ func NewRepoRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
// Print table of repo info
func printRepoTable(repos appsv1.Repositories) {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
_, _ = fmt.Fprintf(w, "TYPE\tNAME\tREPO\tINSECURE\tLFS\tCREDS\tSTATUS\tMESSAGE\n")
_, _ = fmt.Fprintf(w, "TYPE\tNAME\tREPO\tINSECURE\tOCI\tLFS\tCREDS\tSTATUS\tMESSAGE\n")
for _, r := range repos {
var hasCreds string
if !r.HasCredentials() {
@@ -221,7 +228,7 @@ func printRepoTable(repos appsv1.Repositories) {
hasCreds = "true"
}
}
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%v\t%v\t%s\t%s\t%s\n", r.Type, r.Name, r.Repo, r.IsInsecure(), r.EnableLFS, hasCreds, r.ConnectionState.Status, r.ConnectionState.Message)
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%v\t%v\t%v\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)
}
_ = w.Flush()
}

View File

@@ -7,8 +7,6 @@ import (
"os"
"text/tabwriter"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
"github.com/argoproj/gitops-engine/pkg/utils/io"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -16,7 +14,9 @@ import (
repocredspkg "github.com/argoproj/argo-cd/pkg/apiclient/repocreds"
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/git"
"github.com/argoproj/argo-cd/util/io"
)
// NewRepoCredsCommand returns a new instance of an `argocd repocreds` command

View File

@@ -1,13 +1,13 @@
package commands
import (
"github.com/argoproj/gitops-engine/pkg/utils/errors"
"github.com/spf13/cobra"
"k8s.io/client-go/tools/clientcmd"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/config"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/localconfig"
)
@@ -38,6 +38,7 @@ func NewCommand() *cobra.Command {
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
},
DisableAutoGenTag: true,
}
command.AddCommand(NewCompletionCommand())

View File

@@ -8,12 +8,11 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
argoio "github.com/argoproj/gitops-engine/pkg/utils/io"
"github.com/argoproj/argo-cd/common"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
"github.com/argoproj/argo-cd/pkg/apiclient/version"
"github.com/argoproj/argo-cd/util/errors"
argoio "github.com/argoproj/argo-cd/util/io"
)
// NewVersionCmd returns a new `version` command to be used as a sub-command to root
@@ -130,4 +129,5 @@ func printServerVersion(version *version.VersionMessage, short bool) {
fmt.Printf(" Kustomize Version: %s\n", version.KustomizeVersion)
fmt.Printf(" Helm Version: %s\n", version.HelmVersion)
fmt.Printf(" Kubectl Version: %s\n", version.KubectlVersion)
fmt.Printf(" Jsonnet Version: %s\n", version.JsonnetVersion)
}

View File

@@ -1,14 +1,15 @@
package main
import (
"github.com/argoproj/gitops-engine/pkg/utils/errors"
commands "github.com/argoproj/argo-cd/cmd/argocd/commands"
"github.com/argoproj/argo-cd/util/errors"
// load the gcp plugin (required to authenticate against GKE clusters).
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
// load the oidc plugin (required to authenticate with OpenID Connect).
_ "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"
)
func main() {

View File

@@ -85,6 +85,8 @@ const (
DexAPIEndpoint = "/api/dex"
// LoginEndpoint is Argo CD's shorthand login endpoint which redirects to dex's OAuth 2.0 provider's consent page
LoginEndpoint = "/auth/login"
// LogoutEndpoint is Argo CD's shorthand logout endpoint which invalidates OIDC session after logout
LogoutEndpoint = "/auth/logout"
// CallbackEndpoint is Argo CD's final callback endpoint we reach after OAuth 2.0 login flow has been completed
CallbackEndpoint = "/auth/callback"
// DexCallbackEndpoint is Argo CD's final callback endpoint when Dex is configured
@@ -123,6 +125,22 @@ const (
AnnotationValueManagedByArgoCD = "argocd.argoproj.io"
// ResourcesFinalizerName the finalizer value which we inject to finalize deletion of an application
ResourcesFinalizerName = "resources-finalizer.argocd.argoproj.io"
// AnnotationKeyManifestGeneratePaths is an annotation that contains a list of semicolon-separated paths in the
// manifests repository that affects the manifest generation. Paths might be either relative or absolute. The
// absolute path means an absolute path within the repository and the relative path is relative to the application
// source path within the repository.
AnnotationKeyManifestGeneratePaths = "argocd.argoproj.io/manifest-generate-paths"
// AnnotationKeyLinkPrefix tells the UI to add an external link icon to the application node
// that links to the value given in the annotation.
// The annotation key must be followed by a unique identifier. Ex: link.argocd.argoproj.io/dashboard
// It's valid to have multiple annotations that match the prefix.
// Values can simply be a url or they can have
// an optional link title separated by a "|"
// Ex: "http://grafana.example.com/d/yu5UH4MMz/deployments"
// Ex: "Go to Dashboard|http://grafana.example.com/d/yu5UH4MMz/deployments"
AnnotationKeyLinkPrefix = "link.argocd.argoproj.io/"
)
// Environment variables for tuning and debugging Argo CD
@@ -152,6 +170,20 @@ const (
EnvK8sClientMaxIdleConnections = "ARGOCD_K8S_CLIENT_MAX_IDLE_CONNECTIONS"
// EnvGnuPGHome is the path to ArgoCD's GnuPG keyring for signature verification
EnvGnuPGHome = "ARGOCD_GNUPGHOME"
// EnvWatchAPIBufferSize is the buffer size used to transfer K8S watch events to watch API consumer
EnvWatchAPIBufferSize = "ARGOCD_WATCH_API_BUFFER_SIZE"
// EnvPauseGenerationAfterFailedAttempts will pause manifest generation after the specified number of failed generation attempts
EnvPauseGenerationAfterFailedAttempts = "ARGOCD_PAUSE_GEN_AFTER_FAILED_ATTEMPTS"
// EnvPauseGenerationMinutes pauses manifest generation for the specified number of minutes, after sufficient manifest generation failures
EnvPauseGenerationMinutes = "ARGOCD_PAUSE_GEN_MINUTES"
// EnvPauseGenerationRequests pauses manifest generation for the specified number of requests, after sufficient manifest generation failures
EnvPauseGenerationRequests = "ARGOCD_PAUSE_GEN_REQUESTS"
// EnvControllerReplicas is the number of controller replicas
EnvControllerReplicas = "ARGOCD_CONTROLLER_REPLICAS"
// EnvControllerShard is the shard number that should be handled by controller
EnvControllerShard = "ARGOCD_CONTROLLER_SHARD"
// EnvEnableGRPCTimeHistogramEnv enables gRPC metrics collection
EnvEnableGRPCTimeHistogramEnv = "ARGOCD_ENABLE_GRPC_TIME_HISTOGRAM"
)
const (
@@ -161,7 +193,7 @@ const (
MinClientVersion = "1.4.0"
// CacheVersion is a objects version cached using util/cache/cache.go.
// Number should be bumped in case of backward incompatible change to make sure cache is invalidated after upgrade.
CacheVersion = "1.0.0"
CacheVersion = "1.8.3"
)
// GetGnuPGHomePath retrieves the path to use for GnuPG home directory, which is either taken from GNUPGHOME environment or a default value

View File

@@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"math"
"net/http"
"reflect"
"runtime/debug"
"sort"
@@ -16,9 +17,8 @@ import (
"github.com/argoproj/gitops-engine/pkg/diff"
"github.com/argoproj/gitops-engine/pkg/health"
synccommon "github.com/argoproj/gitops-engine/pkg/sync/common"
"github.com/argoproj/gitops-engine/pkg/utils/errors"
"github.com/argoproj/gitops-engine/pkg/utils/io"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
jsonpatch "github.com/evanphx/json-patch"
log "github.com/sirupsen/logrus"
"golang.org/x/sync/semaphore"
v1 "k8s.io/api/core/v1"
@@ -26,9 +26,11 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
apiruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
@@ -42,14 +44,15 @@ import (
"github.com/argoproj/argo-cd/pkg/apis/application"
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
appinformers "github.com/argoproj/argo-cd/pkg/client/informers/externalversions"
"github.com/argoproj/argo-cd/pkg/client/informers/externalversions/application/v1alpha1"
applisters "github.com/argoproj/argo-cd/pkg/client/listers/application/v1alpha1"
"github.com/argoproj/argo-cd/reposerver/apiclient"
"github.com/argoproj/argo-cd/util/argo"
appstatecache "github.com/argoproj/argo-cd/util/cache/appstate"
"github.com/argoproj/argo-cd/util/db"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/glob"
logutils "github.com/argoproj/argo-cd/util/log"
settings_util "github.com/argoproj/argo-cd/util/settings"
)
@@ -106,11 +109,7 @@ type ApplicationController struct {
refreshRequestedAppsMutex *sync.Mutex
metricsServer *metrics.MetricsServer
kubectlSemaphore *semaphore.Weighted
}
type ApplicationControllerConfig struct {
InstanceID string
Namespace string
clusterFilter func(cluster *appv1.Cluster) bool
}
// NewApplicationController creates new instance of ApplicationController.
@@ -126,6 +125,7 @@ func NewApplicationController(
selfHealTimeout time.Duration,
metricsPort int,
kubectlParallelismLimit int64,
clusterFilter func(cluster *appv1.Cluster) bool,
) (*ApplicationController, error) {
log.Infof("appResyncPeriod=%v", appResyncPeriod)
db := db.NewDB(namespace, settingsMgr, kubeClientset)
@@ -147,15 +147,13 @@ func NewApplicationController(
auditLogger: argo.NewAuditLogger(namespace, kubeClientset, "argocd-application-controller"),
settingsMgr: settingsMgr,
selfHealTimeout: selfHealTimeout,
clusterFilter: clusterFilter,
}
if kubectlParallelismLimit > 0 {
ctrl.kubectlSemaphore = semaphore.NewWeighted(kubectlParallelismLimit)
}
kubectl.SetOnKubectlRun(ctrl.onKubectlRun)
appInformer, appLister, err := ctrl.newApplicationInformerAndLister()
if err != nil {
return nil, err
}
appInformer, appLister := ctrl.newApplicationInformerAndLister()
indexers := cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}
projInformer := v1alpha1.NewAppProjectInformer(applicationClientset, namespace, appResyncPeriod, indexers)
projInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
@@ -176,11 +174,14 @@ func NewApplicationController(
},
})
metricsAddr := fmt.Sprintf("0.0.0.0:%d", metricsPort)
ctrl.metricsServer = metrics.NewMetricsServer(metricsAddr, appLister, func() error {
_, err := kubeClientset.Discovery().ServerVersion()
return err
var err error
ctrl.metricsServer, err = metrics.NewMetricsServer(metricsAddr, appLister, ctrl.canProcessApp, func(r *http.Request) error {
return nil
})
stateCache := statecache.NewLiveStateCache(db, appInformer, ctrl.settingsMgr, kubectl, ctrl.metricsServer, ctrl.handleObjectUpdated)
if err != nil {
return nil, err
}
stateCache := statecache.NewLiveStateCache(db, appInformer, ctrl.settingsMgr, kubectl, ctrl.metricsServer, ctrl.handleObjectUpdated, clusterFilter)
appStateManager := NewAppStateManager(db, applicationClientset, repoClientset, namespace, kubectl, ctrl.settingsMgr, stateCache, projInformer, ctrl.metricsServer)
ctrl.appInformer = appInformer
ctrl.appLister = appLister
@@ -195,7 +196,7 @@ func (ctrl *ApplicationController) GetMetricsServer() *metrics.MetricsServer {
return ctrl.metricsServer
}
func (ctrl *ApplicationController) onKubectlRun(command string) (io.Closer, error) {
func (ctrl *ApplicationController) onKubectlRun(command string) (kube.CleanupFunc, error) {
ctrl.metricsServer.IncKubectlExec(command)
if ctrl.kubectlSemaphore != nil {
if err := ctrl.kubectlSemaphore.Acquire(context.Background(), 1); err != nil {
@@ -203,13 +204,12 @@ func (ctrl *ApplicationController) onKubectlRun(command string) (io.Closer, erro
}
ctrl.metricsServer.IncKubectlExecPending(command)
}
return io.NewCloser(func() error {
return func() {
if ctrl.kubectlSemaphore != nil {
ctrl.kubectlSemaphore.Release(1)
ctrl.metricsServer.DecKubectlExecPending(command)
}
return nil
}), nil
}, nil
}
func isSelfReferencedApp(app *appv1.Application, ref v1.ObjectReference) bool {
@@ -222,13 +222,13 @@ func isSelfReferencedApp(app *appv1.Application, ref v1.ObjectReference) bool {
}
func (ctrl *ApplicationController) getAppProj(app *appv1.Application) (*appv1.AppProject, error) {
return argo.GetAppProject(&app.Spec, applisters.NewAppProjectLister(ctrl.projInformer.GetIndexer()), ctrl.namespace)
return argo.GetAppProject(&app.Spec, applisters.NewAppProjectLister(ctrl.projInformer.GetIndexer()), ctrl.namespace, ctrl.settingsMgr)
}
func (ctrl *ApplicationController) handleObjectUpdated(managedByApp map[string]bool, ref v1.ObjectReference) {
// if namespaced resource is not managed by any app it might be orphaned resource of some other apps
if len(managedByApp) == 0 && ref.Namespace != "" {
// retrieve applications which monitor orphaned resources in the same namespace and refresh them unless resource is blacklisted in app project
// retrieve applications which monitor orphaned resources in the same namespace and refresh them unless resource is denied in app project
if objs, err := ctrl.appInformer.GetIndexer().ByIndex(orphanedIndex, ref.Namespace); err == nil {
for i := range objs {
app, ok := objs[i].(*appv1.Application)
@@ -251,6 +251,11 @@ func (ctrl *ApplicationController) handleObjectUpdated(managedByApp map[string]b
continue
}
if !ctrl.canProcessApp(obj) {
// Don't force refresh app if app belongs to a different controller shard
continue
}
level := ComparisonWithNothing
if isManagedResource {
level = CompareWithRecent
@@ -299,7 +304,7 @@ func isKnownOrphanedResourceExclusion(key kube.ResourceKey, proj *appv1.AppProje
func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managedResources []*appv1.ResourceDiff) (*appv1.ApplicationTree, error) {
nodes := make([]appv1.ResourceNode, 0)
proj, err := argo.GetAppProject(&a.Spec, applisters.NewAppProjectLister(ctrl.projInformer.GetIndexer()), ctrl.namespace)
proj, err := argo.GetAppProject(&a.Spec, applisters.NewAppProjectLister(ctrl.projInformer.GetIndexer()), ctrl.namespace, ctrl.settingsMgr)
if err != nil {
return nil, err
}
@@ -404,7 +409,10 @@ func (ctrl *ApplicationController) managedResources(comparisonResult *comparison
if err != nil {
return nil, err
}
resDiffPtr, err := diff.Diff(target, live, comparisonResult.diffNormalizer, compareOptions)
resDiffPtr, err := diff.Diff(target, live,
diff.WithNormalizer(comparisonResult.diffNormalizer),
diff.WithLogr(logutils.NewLogrusLogger(log.New())),
diff.IgnoreAggregatedRoles(compareOptions.IgnoreAggregatedRoles))
if err != nil {
return nil, err
}
@@ -501,8 +509,10 @@ func (ctrl *ApplicationController) requestAppRefresh(appName string, compareWith
}
if after != nil {
ctrl.appRefreshQueue.AddAfter(key, *after)
ctrl.appOperationQueue.AddAfter(key, *after)
} else {
ctrl.appRefreshQueue.Add(key)
ctrl.appOperationQueue.Add(key)
}
}
}
@@ -540,11 +550,13 @@ func (ctrl *ApplicationController) processAppOperationQueueItem() (processNext b
// This happens after app was deleted, but the work queue still had an entry for it.
return
}
app, ok := obj.(*appv1.Application)
origApp, ok := obj.(*appv1.Application)
if !ok {
log.Warnf("Key '%s' in index is not an application", appKey)
return
}
app := origApp.DeepCopy()
if app.Operation != nil {
ctrl.processRequestedAppOperation(app)
} else if app.DeletionTimestamp != nil && app.CascadedDeletion() {
@@ -770,6 +782,13 @@ func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Applic
}
func (ctrl *ApplicationController) setAppCondition(app *appv1.Application, condition appv1.ApplicationCondition) {
// do nothing if app already has same condition
for _, c := range app.Status.Conditions {
if c.Message == condition.Message && c.Type == condition.Type {
return
}
}
app.Status.SetConditions([]appv1.ApplicationCondition{condition}, map[appv1.ApplicationConditionType]bool{condition.Type: true})
var patch []byte
@@ -835,6 +854,11 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli
ctrl.requestAppRefresh(app.Name, CompareWithLatest.Pointer(), &retryAfter)
return
} else {
// retrying operation. remove previous failure time in app since it is used as a trigger
// that previous failed and operation should be retried
state.FinishedAt = nil
ctrl.setOperationState(app, state)
// Get rid of sync results and null out previous operation completion time
state.SyncResult = nil
}
} else {
@@ -898,7 +922,7 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli
}
func (ctrl *ApplicationController) setOperationState(app *appv1.Application, state *appv1.OperationState) {
kube.RetryUntilSucceed(context.Background(), updateOperationStateTimeout, "Update application operation state", func() error {
kube.RetryUntilSucceed(context.Background(), updateOperationStateTimeout, "Update application operation state", logutils.NewLogrusLogger(log.New()), func() error {
if state.Phase == "" {
// expose any bugs where we neglect to set phase
panic("no phase was set")
@@ -925,6 +949,13 @@ func (ctrl *ApplicationController) setOperationState(app *appv1.Application, sta
if err != nil {
return 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 err
}
}
appClient := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(ctrl.namespace)
_, err = appClient.Patch(context.Background(), app.Name, types.MergePatchType, patchJSON, metav1.PatchOptions{})
if err != nil {
@@ -1016,23 +1047,14 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
logCtx.Warnf("Failed to get cached managed resources for tree reconciliation, fallback to full reconciliation")
} else {
var tree *appv1.ApplicationTree
if err = argo.ValidateDestination(context.Background(), &app.Spec.Destination, ctrl.db); err == nil {
if tree, err = ctrl.getResourceTree(app, managedResources); err == nil {
app.Status.Summary = tree.GetSummary()
if err := ctrl.cache.SetAppResourcesTree(app.Name, tree); err != nil {
logCtx.Errorf("Failed to cache resources tree: %v", err)
return
}
if tree, err = ctrl.getResourceTree(app, managedResources); err == nil {
app.Status.Summary = tree.GetSummary()
if err := ctrl.cache.SetAppResourcesTree(app.Name, tree); err != nil {
logCtx.Errorf("Failed to cache resources tree: %v", err)
return
}
} else {
app.Status.SetConditions([]appv1.ApplicationCondition{{
Type: appv1.ApplicationConditionComparisonError, Message: err.Error(),
}}, map[appv1.ApplicationConditionType]bool{
appv1.ApplicationConditionComparisonError: true,
})
}
now := metav1.Now()
app.Status.ObservedAt = &now
ctrl.persistAppStatus(origApp, &app.Status)
return
}
@@ -1056,7 +1078,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
revision = app.Status.Sync.Revision
}
observedAt := metav1.Now()
now := metav1.Now()
compareResult := ctrl.appStateManager.CompareAppState(app, project, revision, app.Spec.Source, refreshType == appv1.RefreshTypeHard, localManifests)
for k, v := range compareResult.timings {
logCtx = logCtx.WithField(k, v.Milliseconds())
@@ -1089,9 +1111,8 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
}
if app.Status.ReconciledAt == nil || comparisonLevel == CompareWithLatest {
app.Status.ReconciledAt = &observedAt
app.Status.ReconciledAt = &now
}
app.Status.ObservedAt = &observedAt
app.Status.Sync = *compareResult.syncStatus
app.Status.Health = *compareResult.healthStatus
app.Status.Resources = compareResult.resources
@@ -1163,13 +1184,6 @@ func (ctrl *ApplicationController) refreshAppConditions(app *appv1.Application)
})
}
} else {
if err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, ctrl.db); err != nil {
errorConditions = append(errorConditions, appv1.ApplicationCondition{
Message: err.Error(),
Type: appv1.ApplicationConditionInvalidSpecError,
})
}
specConditions, err := argo.ValidatePermissions(context.Background(), &app.Spec, proj, ctrl.db)
if err != nil {
errorConditions = append(errorConditions, appv1.ApplicationCondition{
@@ -1327,7 +1341,7 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *
}
if app.Spec.SyncPolicy.Automated.Prune {
if app.Spec.SyncPolicy.Automated.Prune && !app.Spec.SyncPolicy.Automated.AllowEmpty {
bAllNeedPrune := true
for _, r := range resources {
if !r.RequiresPruning {
@@ -1335,7 +1349,7 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *
}
}
if bAllNeedPrune {
message := fmt.Sprintf("Skipping sync attempt to %s: auto-sync will wipe out all resourses", desiredCommitSHA)
message := fmt.Sprintf("Skipping sync attempt to %s: auto-sync will wipe out all resources", desiredCommitSHA)
logCtx.Warnf(message)
return &appv1.ApplicationCondition{Type: appv1.ApplicationConditionSyncError, Message: message}
}
@@ -1385,18 +1399,69 @@ func (ctrl *ApplicationController) shouldSelfHeal(app *appv1.Application) (bool,
return retryAfter <= 0, retryAfter
}
func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.SharedIndexInformer, applisters.ApplicationLister, error) {
appInformerFactory := appinformers.NewFilteredSharedInformerFactory(
ctrl.applicationClientset,
func (ctrl *ApplicationController) canProcessApp(obj interface{}) bool {
app, ok := obj.(*appv1.Application)
if !ok {
return false
}
if ctrl.clusterFilter != nil {
cluster, err := ctrl.db.GetCluster(context.Background(), app.Spec.Destination.Server)
if err != nil {
return ctrl.clusterFilter(nil)
}
return ctrl.clusterFilter(cluster)
}
return true
}
func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.SharedIndexInformer, applisters.ApplicationLister) {
informer := cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (apiruntime.Object, error) {
return ctrl.applicationClientset.ArgoprojV1alpha1().Applications(ctrl.namespace).List(context.TODO(), options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
return ctrl.applicationClientset.ArgoprojV1alpha1().Applications(ctrl.namespace).Watch(context.TODO(), options)
},
},
&appv1.Application{},
ctrl.statusRefreshTimeout,
ctrl.namespace,
func(options *metav1.ListOptions) {},
cache.Indexers{
cache.NamespaceIndex: func(obj interface{}) ([]string, error) {
app, ok := obj.(*appv1.Application)
if ok {
if err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, ctrl.db); err != nil {
ctrl.setAppCondition(app, appv1.ApplicationCondition{Type: appv1.ApplicationConditionInvalidSpecError, Message: err.Error()})
}
}
return cache.MetaNamespaceIndexFunc(obj)
},
orphanedIndex: func(obj interface{}) (i []string, e error) {
app, ok := obj.(*appv1.Application)
if !ok {
return nil, nil
}
proj, err := ctrl.getAppProj(app)
if err != nil {
return nil, nil
}
if proj.Spec.OrphanedResources != nil {
return []string{app.Spec.Destination.Namespace}, nil
}
return nil, nil
},
},
)
informer := appInformerFactory.Argoproj().V1alpha1().Applications().Informer()
lister := appInformerFactory.Argoproj().V1alpha1().Applications().Lister()
lister := applisters.NewApplicationLister(informer.GetIndexer())
informer.AddEventHandler(
cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
if !ctrl.canProcessApp(obj) {
return
}
key, err := cache.MetaNamespaceKeyFunc(obj)
if err == nil {
ctrl.appRefreshQueue.Add(key)
@@ -1404,6 +1469,10 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar
}
},
UpdateFunc: func(old, new interface{}) {
if !ctrl.canProcessApp(new) {
return
}
key, err := cache.MetaNamespaceKeyFunc(new)
if err != nil {
return
@@ -1419,6 +1488,9 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar
ctrl.appOperationQueue.Add(key)
},
DeleteFunc: func(obj interface{}) {
if !ctrl.canProcessApp(obj) {
return
}
// IndexerInformer uses a delta queue, therefore for deletes we have to use this
// key function.
key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
@@ -1428,28 +1500,11 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar
},
},
)
err := informer.AddIndexers(cache.Indexers{
orphanedIndex: func(obj interface{}) (i []string, e error) {
app, ok := obj.(*appv1.Application)
if !ok {
return nil, nil
}
proj, err := ctrl.getAppProj(app)
if err != nil {
return nil, nil
}
if proj.Spec.OrphanedResources != nil {
return []string{app.Spec.Destination.Namespace}, nil
}
return nil, nil
},
})
return informer, lister, err
return informer, lister
}
func (ctrl *ApplicationController) RegisterClusterSecretUpdater(ctx context.Context) {
updater := NewClusterInfoUpdater(ctrl.stateCache, ctrl.db, ctrl.appLister.Applications(ctrl.namespace), ctrl.cache)
updater := NewClusterInfoUpdater(ctrl.stateCache, ctrl.db, ctrl.appLister.Applications(ctrl.namespace), ctrl.cache, ctrl.clusterFilter)
go updater.Run(ctx)
}

View File

@@ -98,6 +98,7 @@ func newFakeController(data *fakeData) *ApplicationController {
time.Minute,
common.DefaultPortArgoCDMetrics,
0,
nil,
)
if err != nil {
panic(err)
@@ -288,6 +289,31 @@ func TestAutoSync(t *testing.T) {
assert.False(t, app.Operation.Sync.Prune)
}
func TestAutoSyncNotAllowEmpty(t *testing.T) {
app := newFakeApp()
app.Spec.SyncPolicy.Automated.Prune = true
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}})
syncStatus := argoappv1.SyncStatus{
Status: argoappv1.SyncStatusCodeOutOfSync,
Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
}
cond := ctrl.autoSync(app, &syncStatus, []argoappv1.ResourceStatus{})
assert.NotNil(t, cond)
}
func TestAutoSyncAllowEmpty(t *testing.T) {
app := newFakeApp()
app.Spec.SyncPolicy.Automated.Prune = true
app.Spec.SyncPolicy.Automated.AllowEmpty = true
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}})
syncStatus := argoappv1.SyncStatus{
Status: argoappv1.SyncStatusCodeOutOfSync,
Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
}
cond := ctrl.autoSync(app, &syncStatus, []argoappv1.ResourceStatus{})
assert.Nil(t, cond)
}
func TestSkipAutoSync(t *testing.T) {
// Verify we skip when we previously synced to it in our most recent history
// Set current to 'aaaaa', desired to 'aaaa' and mark system OutOfSync
@@ -576,15 +602,11 @@ func TestFinalizeAppDeletion(t *testing.T) {
})
t.Run("DeleteWithDestinationClusterName", func(t *testing.T) {
app := newFakeApp()
app.Spec.Destination.Namespace = test.FakeArgoCDNamespace
app.Spec.Destination.Name = "minikube"
app.Spec.Destination.Server = ""
app := newFakeAppWithDestName()
appObj := kube.MustToUnstructured(&app)
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &defaultProj}, managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
kube.GetResourceKey(appObj): appObj,
}})
patched := false
fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset)
defaultReactor := fakeAppCs.ReactionChain[0]
@@ -600,6 +622,27 @@ func TestFinalizeAppDeletion(t *testing.T) {
assert.NoError(t, err)
assert.True(t, patched)
})
t.Run("ErrorOnBothDestNameAndServer", func(t *testing.T) {
app := newFakeAppWithDestMismatch()
appObj := kube.MustToUnstructured(&app)
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &defaultProj}, managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
kube.GetResourceKey(appObj): appObj,
}})
fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset)
func() {
fakeAppCs.Lock()
defer fakeAppCs.Unlock()
defaultReactor := fakeAppCs.ReactionChain[0]
fakeAppCs.ReactionChain = nil
fakeAppCs.AddReactor("get", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
return defaultReactor.React(action)
})
}()
_, err := ctrl.finalizeApplicationDeletion(app)
assert.EqualError(t, err, "application destination can't have both name and server defined: another-cluster https://localhost:6443")
})
}
// TestNormalizeApplication verifies we normalize an application during reconciliation
@@ -901,26 +944,6 @@ func TestRefreshAppConditions(t *testing.T) {
assert.Equal(t, argoappv1.ApplicationConditionInvalidSpecError, app.Status.Conditions[0].Type)
assert.Equal(t, "Application referencing project wrong project which does not exist", app.Status.Conditions[0].Message)
})
t.Run("NoErrorConditionsWithDestNameOnly", func(t *testing.T) {
app := newFakeAppWithDestName()
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &defaultProj}})
_, hasErrors := ctrl.refreshAppConditions(app)
assert.False(t, hasErrors)
assert.Len(t, app.Status.Conditions, 0)
})
t.Run("ErrorOnBothDestNameAndServer", func(t *testing.T) {
app := newFakeAppWithDestMismatch()
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &defaultProj}})
_, hasErrors := ctrl.refreshAppConditions(app)
assert.True(t, hasErrors)
assert.Len(t, app.Status.Conditions, 1)
assert.Equal(t, argoappv1.ApplicationConditionInvalidSpecError, app.Status.Conditions[0].Type)
assert.Equal(t, "application destination can't have both name and server defined: another-cluster https://localhost:6443", app.Status.Conditions[0].Message)
})
}
func TestUpdateReconciledAt(t *testing.T) {
@@ -962,7 +985,7 @@ func TestUpdateReconciledAt(t *testing.T) {
_, updated, err = unstructured.NestedString(receivedPatch, "status", "observedAt")
assert.NoError(t, err)
assert.True(t, updated)
assert.False(t, updated)
})
t.Run("NotUpdatedOnPartialReconciliation", func(t *testing.T) {
@@ -978,7 +1001,7 @@ func TestUpdateReconciledAt(t *testing.T) {
_, updated, err = unstructured.NestedString(receivedPatch, "status", "observedAt")
assert.NoError(t, err)
assert.True(t, updated)
assert.False(t, updated)
})
}
@@ -1044,6 +1067,34 @@ func TestProcessRequestedAppOperation_FailedNoRetries(t *testing.T) {
assert.Equal(t, string(synccommon.OperationError), phase)
}
func TestProcessRequestedAppOperation_InvalidDestination(t *testing.T) {
app := newFakeAppWithDestMismatch()
app.Spec.Project = "test-project"
app.Operation = &argoappv1.Operation{
Sync: &argoappv1.SyncOperation{},
}
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}})
fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset)
receivedPatch := map[string]interface{}{}
func() {
fakeAppCs.Lock()
defer fakeAppCs.Unlock()
fakeAppCs.PrependReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
if patchAction, ok := action.(kubetesting.PatchAction); ok {
assert.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch))
}
return true, nil, nil
})
}()
ctrl.processRequestedAppOperation(app)
phase, _, _ := unstructured.NestedString(receivedPatch, "status", "operationState", "phase")
assert.Equal(t, string(synccommon.OperationFailed), phase)
message, _, _ := unstructured.NestedString(receivedPatch, "status", "operationState", "message")
assert.Contains(t, message, "application destination can't have both name and server defined: another-cluster https://localhost:6443")
}
func TestProcessRequestedAppOperation_FailedHasRetries(t *testing.T) {
app := newFakeApp()
app.Spec.Project = "invalid-project"

View File

@@ -2,6 +2,7 @@ package cache
import (
"context"
"fmt"
"reflect"
"sync"
@@ -9,6 +10,7 @@ import (
"github.com/argoproj/gitops-engine/pkg/health"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
log "github.com/sirupsen/logrus"
"golang.org/x/sync/semaphore"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -21,6 +23,7 @@ import (
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/argo"
"github.com/argoproj/argo-cd/util/db"
logutils "github.com/argoproj/argo-cd/util/log"
"github.com/argoproj/argo-cd/util/lua"
"github.com/argoproj/argo-cd/util/settings"
)
@@ -63,7 +66,8 @@ func NewLiveStateCache(
settingsMgr *settings.SettingsManager,
kubectl kube.Kubectl,
metricsServer *metrics.MetricsServer,
onObjectUpdated ObjectUpdatedHandler) LiveStateCache {
onObjectUpdated ObjectUpdatedHandler,
clusterFilter func(cluster *appv1.Cluster) bool) LiveStateCache {
return &liveStateCache{
appInformer: appInformer,
@@ -73,6 +77,9 @@ func NewLiveStateCache(
kubectl: kubectl,
settingsMgr: settingsMgr,
metricsServer: metricsServer,
// The default limit of 50 is chosen based on experiments.
listSemaphore: semaphore.NewWeighted(50),
clusterFilter: clusterFilter,
}
}
@@ -88,6 +95,11 @@ type liveStateCache struct {
kubectl kube.Kubectl
settingsMgr *settings.SettingsManager
metricsServer *metrics.MetricsServer
clusterFilter func(cluster *appv1.Cluster) bool
// listSemaphore is used to limit the number of concurrent memory consuming operations on the
// k8s list queries results across all clusters to avoid memory spikes during cache initialization.
listSemaphore *semaphore.Weighted
clusters map[string]clustercache.ClusterCache
cacheSettings cacheSettings
@@ -231,7 +243,12 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e
return nil, err
}
if !c.canHandleCluster(cluster) {
return nil, fmt.Errorf("controller is configured to ignore cluster %s", cluster.Server)
}
clusterCache = clustercache.NewClusterCache(cluster.RESTConfig(),
clustercache.SetListSemaphore(c.listSemaphore),
clustercache.SetResyncTimeout(common.K8SClusterResyncDuration),
clustercache.SetSettings(cacheSettings.clusterSettings),
clustercache.SetNamespaces(cluster.Namespaces),
@@ -248,6 +265,7 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e
// want the full resource to be available in our cache (to diff), so we store all CRDs
return res, res.AppName != "" || un.GroupVersionKind().Kind == kube.CustomResourceDefinitionKind
}),
clustercache.SetLogr(logutils.NewLogrusLogger(log.WithField("server", cluster.Server))),
)
_ = clusterCache.OnResourceUpdated(func(newRes *clustercache.Resource, oldRes *clustercache.Resource, namespaceResources map[kube.ResourceKey]*clustercache.Resource) {
@@ -276,7 +294,7 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e
c.metricsServer.IncClusterEventsCount(cluster.Server, gvk.Group, gvk.Kind)
})
c.clusters[cluster.Server] = clusterCache
c.clusters[server] = clusterCache
return clusterCache, nil
}
@@ -418,7 +436,7 @@ func (c *liveStateCache) Init() error {
func (c *liveStateCache) Run(ctx context.Context) error {
go c.watchSettings(ctx)
kube.RetryUntilSucceed(ctx, clustercache.ClusterRetryTimeout, "watch clusters", func() error {
kube.RetryUntilSucceed(ctx, clustercache.ClusterRetryTimeout, "watch clusters", logutils.NewLogrusLogger(log.New()), func() error {
return c.db.WatchClusters(ctx, c.handleAddEvent, c.handleModEvent, c.handleDeleteEvent)
})
@@ -427,7 +445,19 @@ func (c *liveStateCache) Run(ctx context.Context) error {
return nil
}
func (c *liveStateCache) canHandleCluster(cluster *appv1.Cluster) bool {
if c.clusterFilter == nil {
return true
}
return c.clusterFilter(cluster)
}
func (c *liveStateCache) handleAddEvent(cluster *appv1.Cluster) {
if !c.canHandleCluster(cluster) {
log.Infof("Ignoring cluster %s", cluster.Server)
return
}
c.lock.Lock()
_, ok := c.clusters[cluster.Server]
c.lock.Unlock()
@@ -446,6 +476,14 @@ func (c *liveStateCache) handleModEvent(oldCluster *appv1.Cluster, newCluster *a
cluster, ok := c.clusters[newCluster.Server]
c.lock.Unlock()
if ok {
if !c.canHandleCluster(newCluster) {
cluster.Invalidate()
c.lock.Lock()
delete(c.clusters, newCluster.Server)
c.lock.Unlock()
return
}
var updateSettings []clustercache.UpdateSettingsFunc
if !reflect.DeepEqual(oldCluster.Config, newCluster.Config) {
updateSettings = append(updateSettings, clustercache.SetConfig(newCluster.RESTConfig()))

View File

@@ -3,6 +3,8 @@ package cache
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/argoproj/gitops-engine/pkg/cache"
"github.com/argoproj/gitops-engine/pkg/cache/mocks"
"github.com/stretchr/testify/mock"
@@ -31,6 +33,32 @@ func TestHandleModEvent_HasChanges(t *testing.T) {
})
}
func TestHandleModEvent_ClusterExcluded(t *testing.T) {
clusterCache := &mocks.ClusterCache{}
clusterCache.On("Invalidate", mock.Anything, mock.Anything).Return(nil).Once()
clusterCache.On("EnsureSynced").Return(nil).Once()
clustersCache := liveStateCache{
clusters: map[string]cache.ClusterCache{
"https://mycluster": clusterCache,
},
clusterFilter: func(cluster *appv1.Cluster) bool {
return false
},
}
clustersCache.handleModEvent(&appv1.Cluster{
Server: "https://mycluster",
Config: appv1.ClusterConfig{Username: "foo"},
}, &appv1.Cluster{
Server: "https://mycluster",
Config: appv1.ClusterConfig{Username: "bar"},
Namespaces: []string{"default"},
})
assert.Len(t, clustersCache.clusters, 0)
}
func TestHandleModEvent_NoChanges(t *testing.T) {
clusterCache := &mocks.ClusterCache{}
clusterCache.On("Invalidate", mock.Anything).Panic("should not invalidate")
@@ -50,3 +78,18 @@ func TestHandleModEvent_NoChanges(t *testing.T) {
Config: appv1.ClusterConfig{Username: "bar"},
})
}
func TestHandleAddEvent_ClusterExcluded(t *testing.T) {
clustersCache := liveStateCache{
clusters: map[string]cache.ClusterCache{},
clusterFilter: func(cluster *appv1.Cluster) bool {
return false
},
}
clustersCache.handleAddEvent(&appv1.Cluster{
Server: "https://mycluster",
Config: appv1.ClusterConfig{Username: "bar"},
})
assert.Len(t, clustersCache.clusters, 0)
}

View File

@@ -2,6 +2,7 @@ package cache
import (
"fmt"
"strings"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/argoproj/gitops-engine/pkg/utils/text"
@@ -10,6 +11,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
k8snode "k8s.io/kubernetes/pkg/util/node"
"github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/resource"
)
@@ -36,6 +38,21 @@ func populateNodeInfo(un *unstructured.Unstructured, res *ResourceInfo) {
populateIngressInfo(un, res)
return
}
case "networking.istio.io":
switch gvk.Kind {
case "VirtualService":
populateIstioVirtualServiceInfo(un, res)
return
}
}
for k, v := range un.GetAnnotations() {
if strings.HasPrefix(k, common.AnnotationKeyLinkPrefix) {
if res.NetworkingInfo == nil {
res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{}
}
res.NetworkingInfo.ExternalURLs = append(res.NetworkingInfo.ExternalURLs, v)
}
}
}
@@ -112,36 +129,82 @@ func populateIngressInfo(un *unstructured.Unstructured, res *ResourceInfo) {
}] = true
}
if port, ok, err := unstructured.NestedFieldNoCopy(path, "backend", "servicePort"); ok && err == nil && host != "" && host != nil {
stringPort := ""
switch typedPod := port.(type) {
case int64:
stringPort = fmt.Sprintf("%d", typedPod)
case float64:
stringPort = fmt.Sprintf("%d", int64(typedPod))
case string:
stringPort = typedPod
default:
stringPort = fmt.Sprintf("%v", port)
stringPort := "http"
if tls, ok, err := unstructured.NestedSlice(un.Object, "spec", "tls"); ok && err == nil {
for i := range tls {
tlsline, ok := tls[i].(map[string]interface{})
secretName := tlsline["secretName"]
if ok && secretName != nil {
stringPort = "https"
}
tlshost := tlsline["host"]
if tlshost == host {
stringPort = "https"
}
}
}
externalURL := fmt.Sprintf("%s://%s", stringPort, host)
subPath := ""
if nestedPath, ok, err := unstructured.NestedString(path, "path"); ok && err == nil {
subPath = strings.TrimSuffix(nestedPath, "*")
}
externalURL += subPath
urlsSet[externalURL] = true
}
}
}
targets := make([]v1alpha1.ResourceRef, 0)
for target := range targetsMap {
targets = append(targets, target)
}
var urls []string
if res.NetworkingInfo != nil {
urls = res.NetworkingInfo.ExternalURLs
}
for url := range urlsSet {
urls = append(urls, url)
}
res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{TargetRefs: targets, Ingress: ingress, ExternalURLs: urls}
}
func populateIstioVirtualServiceInfo(un *unstructured.Unstructured, res *ResourceInfo) {
targetsMap := make(map[v1alpha1.ResourceRef]bool)
if rules, ok, err := unstructured.NestedSlice(un.Object, "spec", "http"); ok && err == nil {
for i := range rules {
rule, ok := rules[i].(map[string]interface{})
if !ok {
continue
}
routes, ok, err := unstructured.NestedSlice(rule, "route")
if !ok || err != nil {
continue
}
for i := range routes {
route, ok := routes[i].(map[string]interface{})
if !ok {
continue
}
if hostName, ok, err := unstructured.NestedString(route, "destination", "host"); ok && err == nil {
hostSplits := strings.Split(hostName, ".")
serviceName := hostSplits[0]
var namespace string
if len(hostSplits) >= 2 {
namespace = hostSplits[1]
} else {
namespace = un.GetNamespace()
}
var externalURL string
switch stringPort {
case "80", "http":
externalURL = fmt.Sprintf("http://%s", host)
case "443", "https":
externalURL = fmt.Sprintf("https://%s", host)
default:
externalURL = fmt.Sprintf("http://%s:%s", host, stringPort)
}
subPath := ""
if nestedPath, ok, err := unstructured.NestedString(path, "path"); ok && err == nil {
subPath = nestedPath
}
externalURL += subPath
urlsSet[externalURL] = true
targetsMap[v1alpha1.ResourceRef{
Kind: kube.ServiceKind,
Name: serviceName,
Namespace: namespace,
}] = true
}
}
}
@@ -150,11 +213,8 @@ func populateIngressInfo(un *unstructured.Unstructured, res *ResourceInfo) {
for target := range targetsMap {
targets = append(targets, target)
}
urls := make([]string, 0)
for url := range urlsSet {
urls = append(urls, url)
}
res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{TargetRefs: targets, Ingress: ingress, ExternalURLs: urls}
res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{TargetRefs: targets}
}
func populatePodInfo(un *unstructured.Unstructured, res *ResourceInfo) {

View File

@@ -9,6 +9,7 @@ import (
"github.com/argoproj/pkg/errors"
"github.com/ghodss/yaml"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -63,10 +64,96 @@ var (
serviceName: helm-guestbook
servicePort: https
path: /
tls:
- host: helm-guestbook.com
secretName: my-tls-secret
status:
loadBalancer:
ingress:
- ip: 107.178.210.11`)
testIngressWildCardPath = strToUnstructured(`
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: helm-guestbook
namespace: default
uid: "4"
spec:
backend:
serviceName: not-found-service
servicePort: 443
rules:
- host: helm-guestbook.com
http:
paths:
- backend:
serviceName: helm-guestbook
servicePort: 443
path: /*
- backend:
serviceName: helm-guestbook
servicePort: https
path: /*
tls:
- host: helm-guestbook.com
secretName: my-tls-secret
status:
loadBalancer:
ingress:
- ip: 107.178.210.11`)
testIngressWithoutTls = strToUnstructured(`
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: helm-guestbook
namespace: default
uid: "4"
spec:
backend:
serviceName: not-found-service
servicePort: 443
rules:
- host: helm-guestbook.com
http:
paths:
- backend:
serviceName: helm-guestbook
servicePort: 443
path: /
- backend:
serviceName: helm-guestbook
servicePort: https
path: /
status:
loadBalancer:
ingress:
- ip: 107.178.210.11`)
testIstioVirtualService = strToUnstructured(`
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: hello-world
namespace: demo
spec:
http:
- match:
- uri:
prefix: "/1"
route:
- destination:
host: service_full.demo.svc.cluster.local
- destination:
host: service_namespace.namespace
- match:
- uri:
prefix: "/2"
route:
- destination:
host: service
`)
)
func TestGetPodInfo(t *testing.T) {
@@ -104,6 +191,29 @@ func TestGetServiceInfo(t *testing.T) {
}, info.NetworkingInfo)
}
func TestGetIstioVirtualServiceInfo(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testIstioVirtualService, info)
assert.Equal(t, 0, len(info.Info))
require.NotNil(t, info.NetworkingInfo)
require.NotNil(t, info.NetworkingInfo.TargetRefs)
assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{
Kind: kube.ServiceKind,
Name: "service_full",
Namespace: "demo",
})
assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{
Kind: kube.ServiceKind,
Name: "service_namespace",
Namespace: "namespace",
})
assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{
Kind: kube.ServiceKind,
Name: "service",
Namespace: "demo",
})
}
func TestGetIngressInfo(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testIngress, info)
@@ -128,6 +238,54 @@ func TestGetIngressInfo(t *testing.T) {
}, info.NetworkingInfo)
}
func TestGetIngressInfoWildCardPath(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testIngressWildCardPath, info)
assert.Equal(t, 0, len(info.Info))
sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool {
return strings.Compare(info.NetworkingInfo.TargetRefs[j].Name, info.NetworkingInfo.TargetRefs[i].Name) < 0
})
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
Ingress: []v1.LoadBalancerIngress{{IP: "107.178.210.11"}},
TargetRefs: []v1alpha1.ResourceRef{{
Namespace: "default",
Group: "",
Kind: kube.ServiceKind,
Name: "not-found-service",
}, {
Namespace: "default",
Group: "",
Kind: kube.ServiceKind,
Name: "helm-guestbook",
}},
ExternalURLs: []string{"https://helm-guestbook.com/"},
}, info.NetworkingInfo)
}
func TestGetIngressInfoWithoutTls(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testIngressWithoutTls, info)
assert.Equal(t, 0, len(info.Info))
sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool {
return strings.Compare(info.NetworkingInfo.TargetRefs[j].Name, info.NetworkingInfo.TargetRefs[i].Name) < 0
})
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
Ingress: []v1.LoadBalancerIngress{{IP: "107.178.210.11"}},
TargetRefs: []v1alpha1.ResourceRef{{
Namespace: "default",
Group: "",
Kind: kube.ServiceKind,
Name: "not-found-service",
}, {
Namespace: "default",
Group: "",
Kind: kube.ServiceKind,
Name: "helm-guestbook",
}},
ExternalURLs: []string{"http://helm-guestbook.com/"},
}, info.NetworkingInfo)
}
func TestGetIngressInfoNoHost(t *testing.T) {
ingress := strToUnstructured(`
apiVersion: extensions/v1beta1
@@ -143,6 +301,8 @@ func TestGetIngressInfoNoHost(t *testing.T) {
serviceName: helm-guestbook
servicePort: 443
path: /
tls:
- secretName: my-tls
status:
loadBalancer:
ingress:
@@ -177,6 +337,8 @@ func TestExternalUrlWithSubPath(t *testing.T) {
serviceName: helm-guestbook
servicePort: 443
path: /my/sub/path/
tls:
- secretName: my-tls
status:
loadBalancer:
ingress:
@@ -211,6 +373,8 @@ func TestExternalUrlWithMultipleSubPaths(t *testing.T) {
- backend:
serviceName: helm-guestbook-3
servicePort: 443
tls:
- secretName: my-tls
status:
loadBalancer:
ingress:
@@ -239,6 +403,8 @@ func TestExternalUrlWithNoSubPath(t *testing.T) {
- backend:
serviceName: helm-guestbook
servicePort: 443
tls:
- secretName: my-tls
status:
loadBalancer:
ingress:
@@ -265,6 +431,8 @@ func TestExternalUrlWithNetworkingApi(t *testing.T) {
- backend:
serviceName: helm-guestbook
servicePort: 443
tls:
- secretName: my-tls
status:
loadBalancer:
ingress:

View File

@@ -23,19 +23,21 @@ const (
)
type clusterInfoUpdater struct {
infoSource metrics.HasClustersInfo
db db.ArgoDB
appLister v1alpha1.ApplicationNamespaceLister
cache *appstatecache.Cache
infoSource metrics.HasClustersInfo
db db.ArgoDB
appLister v1alpha1.ApplicationNamespaceLister
cache *appstatecache.Cache
clusterFilter func(cluster *appv1.Cluster) bool
}
func NewClusterInfoUpdater(
infoSource metrics.HasClustersInfo,
db db.ArgoDB,
appLister v1alpha1.ApplicationNamespaceLister,
cache *appstatecache.Cache) *clusterInfoUpdater {
cache *appstatecache.Cache,
clusterFilter func(cluster *appv1.Cluster) bool) *clusterInfoUpdater {
return &clusterInfoUpdater{infoSource, db, appLister, cache}
return &clusterInfoUpdater{infoSource, db, appLister, cache, clusterFilter}
}
func (c *clusterInfoUpdater) Run(ctx context.Context) {
@@ -63,13 +65,24 @@ func (c *clusterInfoUpdater) updateClusters() {
if err != nil {
log.Warnf("Failed to save clusters info: %v", err)
}
_ = kube.RunAllAsync(len(clusters.Items), func(i int) error {
cluster := clusters.Items[i]
var clustersFiltered []appv1.Cluster
if c.clusterFilter == nil {
clustersFiltered = clusters.Items
} else {
for i := range clusters.Items {
if c.clusterFilter(&clusters.Items[i]) {
clustersFiltered = append(clustersFiltered, clusters.Items[i])
}
}
}
_ = kube.RunAllAsync(len(clustersFiltered), func(i int) error {
cluster := clustersFiltered[i]
if err := c.updateClusterInfo(cluster, infoByServer[cluster.Server]); err != nil {
log.Warnf("Failed to save clusters info: %v", err)
}
return nil
})
log.Debugf("Successfully saved info of %d clusters", len(clustersFiltered))
}
func (c *clusterInfoUpdater) updateClusterInfo(cluster appv1.Cluster, info *cache.ClusterInfo) error {

View File

@@ -39,7 +39,7 @@ func TestClusterSecretUpdater(t *testing.T) {
kubeclientset := fake.NewSimpleClientset()
appclientset := appsfake.NewSimpleClientset()
appInfomer := appinformers.NewApplicationInformer(appclientset, "", time.Minute, cache.Indexers{})
appInformer := appinformers.NewApplicationInformer(appclientset, "", time.Minute, cache.Indexers{})
settingsManager := settings.NewSettingsManager(context.Background(), kubeclientset, fakeNamespace)
argoDB := db.NewDB(fakeNamespace, settingsManager, kubeclientset)
ctx, cancel := context.WithCancel(context.Background())
@@ -57,8 +57,8 @@ func TestClusterSecretUpdater(t *testing.T) {
SyncError: test.SyncError,
}
lister := applisters.NewApplicationLister(appInfomer.GetIndexer()).Applications(fakeNamespace)
updater := NewClusterInfoUpdater(nil, argoDB, lister, appCache)
lister := applisters.NewApplicationLister(appInformer.GetIndexer()).Applications(fakeNamespace)
updater := NewClusterInfoUpdater(nil, argoDB, lister, appCache, nil)
err = updater.updateClusterInfo(*cluster, info)
assert.NoError(t, err, "Invoking updateClusterInfo failed.")

View File

@@ -30,6 +30,7 @@ type MetricsServer struct {
reconcileHistogram *prometheus.HistogramVec
redisRequestHistogram *prometheus.HistogramVec
registry *prometheus.Registry
hostname string
}
const (
@@ -91,12 +92,12 @@ var (
kubectlExecCounter = prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "argocd_kubectl_exec_total",
Help: "Number of kubectl executions",
}, []string{"command"})
}, []string{"hostname", "command"})
kubectlExecPendingGauge = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "argocd_kubectl_exec_pending",
Help: "Number of pending kubectl executions",
}, []string{"command"})
}, []string{"hostname", "command"})
reconcileHistogram = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
@@ -118,7 +119,7 @@ var (
Name: "argocd_redis_request_total",
Help: "Number of kubernetes requests executed during application reconciliation.",
},
[]string{"initiator", "failed"},
[]string{"hostname", "initiator", "failed"},
)
redisRequestHistogram = prometheus.NewHistogramVec(
@@ -127,14 +128,18 @@ var (
Help: "Redis requests duration.",
Buckets: []float64{0.01, 0.05, 0.10, 0.25, .5, 1},
},
[]string{"initiator"},
[]string{"hostname", "initiator"},
)
)
// NewMetricsServer returns a new prometheus server which collects application metrics
func NewMetricsServer(addr string, appLister applister.ApplicationLister, healthCheck func() error) *MetricsServer {
func NewMetricsServer(addr string, appLister applister.ApplicationLister, appFilter func(obj interface{}) bool, healthCheck func(r *http.Request) error) (*MetricsServer, error) {
hostname, err := os.Hostname()
if err != nil {
return nil, err
}
mux := http.NewServeMux()
registry := NewAppRegistry(appLister)
registry := NewAppRegistry(appLister, appFilter)
mux.Handle(MetricsPath, promhttp.HandlerFor(prometheus.Gatherers{
// contains app controller specific metrics
registry,
@@ -166,7 +171,8 @@ func NewMetricsServer(addr string, appLister applister.ApplicationLister, health
clusterEventsCounter: clusterEventsCounter,
redisRequestCounter: redisRequestCounter,
redisRequestHistogram: redisRequestHistogram,
}
hostname: hostname,
}, nil
}
func (m *MetricsServer) RegisterClustersInfoSource(ctx context.Context, source HasClustersInfo) {
@@ -184,15 +190,15 @@ func (m *MetricsServer) IncSync(app *argoappv1.Application, state *argoappv1.Ope
}
func (m *MetricsServer) IncKubectlExec(command string) {
m.kubectlExecCounter.WithLabelValues(command).Inc()
m.kubectlExecCounter.WithLabelValues(m.hostname, command).Inc()
}
func (m *MetricsServer) IncKubectlExecPending(command string) {
m.kubectlExecPendingGauge.WithLabelValues(command).Inc()
m.kubectlExecPendingGauge.WithLabelValues(m.hostname, command).Inc()
}
func (m *MetricsServer) DecKubectlExecPending(command string) {
m.kubectlExecPendingGauge.WithLabelValues(command).Dec()
m.kubectlExecPendingGauge.WithLabelValues(m.hostname, command).Dec()
}
// IncClusterEventsCount increments the number of cluster events
@@ -215,12 +221,12 @@ func (m *MetricsServer) IncKubernetesRequest(app *argoappv1.Application, server,
}
func (m *MetricsServer) IncRedisRequest(failed bool) {
m.redisRequestCounter.WithLabelValues("argocd-application-controller", strconv.FormatBool(failed)).Inc()
m.redisRequestCounter.WithLabelValues(m.hostname, "argocd-application-controller", strconv.FormatBool(failed)).Inc()
}
// ObserveRedisRequestDuration observes redis request duration
func (m *MetricsServer) ObserveRedisRequestDuration(duration time.Duration) {
m.redisRequestHistogram.WithLabelValues("argocd-application-controller").Observe(duration.Seconds())
m.redisRequestHistogram.WithLabelValues(m.hostname, "argocd-application-controller").Observe(duration.Seconds())
}
// IncReconcile increments the reconcile counter for an application
@@ -229,20 +235,22 @@ func (m *MetricsServer) IncReconcile(app *argoappv1.Application, duration time.D
}
type appCollector struct {
store applister.ApplicationLister
store applister.ApplicationLister
appFilter func(obj interface{}) bool
}
// NewAppCollector returns a prometheus collector for application metrics
func NewAppCollector(appLister applister.ApplicationLister) prometheus.Collector {
func NewAppCollector(appLister applister.ApplicationLister, appFilter func(obj interface{}) bool) prometheus.Collector {
return &appCollector{
store: appLister,
store: appLister,
appFilter: appFilter,
}
}
// NewAppRegistry creates a new prometheus registry that collects applications
func NewAppRegistry(appLister applister.ApplicationLister) *prometheus.Registry {
func NewAppRegistry(appLister applister.ApplicationLister, appFilter func(obj interface{}) bool) *prometheus.Registry {
registry := prometheus.NewRegistry()
registry.MustRegister(NewAppCollector(appLister))
registry.MustRegister(NewAppCollector(appLister, appFilter))
return registry
}
@@ -261,7 +269,9 @@ func (c *appCollector) Collect(ch chan<- prometheus.Metric) {
return
}
for _, app := range apps {
collectApps(ch, app)
if c.appFilter(app) {
collectApps(ch, app)
}
}
}

View File

@@ -112,10 +112,14 @@ status:
status: Healthy
`
var noOpHealthCheck = func() error {
var noOpHealthCheck = func(r *http.Request) error {
return nil
}
var appFilter = func(obj interface{}) bool {
return true
}
func newFakeApp(fakeAppYAML string) *argoappv1.Application {
var app argoappv1.Application
err := yaml.Unmarshal([]byte(fakeAppYAML), &app)
@@ -146,7 +150,8 @@ func newFakeLister(fakeAppYAMLs ...string) (context.CancelFunc, applister.Applic
func testApp(t *testing.T, fakeAppYAMLs []string, expectedResponse string) {
cancel, appLister := newFakeLister(fakeAppYAMLs...)
defer cancel()
metricsServ := NewMetricsServer("localhost:8082", appLister, noOpHealthCheck)
metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck)
assert.NoError(t, err)
req, err := http.NewRequest("GET", "/metrics", nil)
assert.NoError(t, err)
rr := httptest.NewRecorder()
@@ -217,7 +222,8 @@ argocd_app_sync_status{name="my-app",namespace="argocd",project="important-proje
func TestMetricsSyncCounter(t *testing.T) {
cancel, appLister := newFakeLister()
defer cancel()
metricsServ := NewMetricsServer("localhost:8082", appLister, noOpHealthCheck)
metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck)
assert.NoError(t, err)
appSyncTotal := `
# HELP argocd_app_sync_total Number of application syncs.
@@ -257,7 +263,9 @@ func assertMetricsPrinted(t *testing.T, expectedLines, body string) {
func TestReconcileMetrics(t *testing.T) {
cancel, appLister := newFakeLister()
defer cancel()
metricsServ := NewMetricsServer("localhost:8082", appLister, noOpHealthCheck)
metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck)
assert.NoError(t, err)
appReconcileMetrics := `
# HELP argocd_app_reconcile Application reconciliation performance.
# TYPE argocd_app_reconcile histogram

View File

@@ -0,0 +1,53 @@
package sharding
import (
"fmt"
"hash/fnv"
"os"
"strconv"
"strings"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
)
func InferShard() (int, error) {
hostname, err := os.Hostname()
if err != nil {
return 0, err
}
parts := strings.Split(hostname, "-")
if len(parts) == 0 {
return 0, fmt.Errorf("hostname should ends with shard number separated by '-' but got: %s", hostname)
}
shard, err := strconv.Atoi(parts[len(parts)-1])
if err != nil {
return 0, fmt.Errorf("hostname should ends with shard number separated by '-' but got: %s", hostname)
}
return shard, nil
}
// getShardByID calculates cluster shard as `clusterSecret.UID % replicas count`
func getShardByID(id string, replicas int) int {
if id == "" {
return 0
} else {
h := fnv.New32a()
_, _ = h.Write([]byte(id))
return int(h.Sum32() % uint32(replicas))
}
}
func GetClusterFilter(replicas int, shard int) func(c *v1alpha1.Cluster) bool {
return func(c *v1alpha1.Cluster) bool {
clusterShard := 0
// cluster might be nil if app is using invalid cluster URL, assume shard 0 in this case.
if c != nil {
if c.Shard != nil {
clusterShard = int(*c.Shard)
} else {
clusterShard = getShardByID(c.ID, replicas)
}
}
return clusterShard == shard
}
}

View File

@@ -0,0 +1,29 @@
package sharding
import (
"testing"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/stretchr/testify/assert"
)
func TestGetShardByID_NotEmptyID(t *testing.T) {
assert.Equal(t, 0, getShardByID("1", 2))
assert.Equal(t, 1, getShardByID("2", 2))
assert.Equal(t, 0, getShardByID("3", 2))
assert.Equal(t, 1, getShardByID("4", 2))
}
func TestGetShardByID_EmptyID(t *testing.T) {
shard := getShardByID("", 10)
assert.Equal(t, 0, shard)
}
func TestGetClusterFilter(t *testing.T) {
filter := GetClusterFilter(2, 1)
assert.False(t, filter(&v1alpha1.Cluster{ID: "1"}))
assert.True(t, filter(&v1alpha1.Cluster{ID: "2"}))
assert.False(t, filter(&v1alpha1.Cluster{ID: "3"}))
assert.True(t, filter(&v1alpha1.Cluster{ID: "4"}))
}

View File

@@ -12,7 +12,6 @@ import (
hookutil "github.com/argoproj/gitops-engine/pkg/sync/hook"
"github.com/argoproj/gitops-engine/pkg/sync/ignore"
resourceutil "github.com/argoproj/gitops-engine/pkg/sync/resource"
"github.com/argoproj/gitops-engine/pkg/utils/io"
kubeutil "github.com/argoproj/gitops-engine/pkg/utils/kube"
log "github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -32,6 +31,7 @@ import (
"github.com/argoproj/argo-cd/util/db"
"github.com/argoproj/argo-cd/util/gpg"
argohealth "github.com/argoproj/argo-cd/util/health"
"github.com/argoproj/argo-cd/util/io"
"github.com/argoproj/argo-cd/util/settings"
"github.com/argoproj/argo-cd/util/stats"
)
@@ -266,7 +266,7 @@ func (m *appStateManager) getComparisonSettings(app *appv1.Application) (string,
func verifyGnuPGSignature(revision string, project *appv1.AppProject, manifestInfo *apiclient.ManifestResponse) []appv1.ApplicationCondition {
now := metav1.Now()
conditions := make([]appv1.ApplicationCondition, 0)
// We need to have some data in the verificatin result to parse, otherwise there was no signature
// We need to have some data in the verification result to parse, otherwise there was no signature
if manifestInfo.VerifyResult != "" {
verifyResult, err := gpg.ParseGitCommitVerification(manifestInfo.VerifyResult)
if err != nil {
@@ -413,7 +413,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
if appInstanceName != "" && appInstanceName != app.Name {
conditions = append(conditions, v1alpha1.ApplicationCondition{
Type: v1alpha1.ApplicationConditionSharedResourceWarning,
Message: fmt.Sprintf("%s/%s is part of a different application: %s", liveObj.GetKind(), liveObj.GetName(), appInstanceName),
Message: fmt.Sprintf("%s/%s is part of applications %s and %s", liveObj.GetKind(), liveObj.GetName(), app.Name, appInstanceName),
LastTransitionTime: &now,
})
}
@@ -426,12 +426,15 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
compareOptions, err := m.settingsMgr.GetResourceCompareOptions()
if err != nil {
log.Warnf("Could not get compare options from ConfigMap (assuming defaults): %v", err)
compareOptions = diff.GetDefaultDiffOptions()
compareOptions = settings.GetDefaultDiffOptions()
}
logCtx.Debugf("built managed objects list")
// Do the actual comparison
diffResults, err := diff.DiffArray(reconciliation.Target, reconciliation.Live, diffNormalizer, compareOptions)
diffResults, err := diff.DiffArray(
reconciliation.Target, reconciliation.Live,
diff.WithNormalizer(diffNormalizer),
diff.IgnoreAggregatedRoles(compareOptions.IgnoreAggregatedRoles))
if err != nil {
diffResults = &diff.DiffResultList{}
failedToLoadObjs = true
@@ -538,7 +541,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
}
// 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 oppinion about the result
// 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.
if gpg.IsGPGEnabled() && verifySignature && manifestInfo != nil {
conditions = append(conditions, verifyGnuPGSignature(revision, project, manifestInfo)...)

View File

@@ -3,6 +3,8 @@ package controller
import (
"context"
"fmt"
"os"
"strconv"
"sync/atomic"
"time"
@@ -14,16 +16,24 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
cdcommon "github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/controller/metrics"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
listersv1alpha1 "github.com/argoproj/argo-cd/pkg/client/listers/application/v1alpha1"
"github.com/argoproj/argo-cd/util/argo"
logutils "github.com/argoproj/argo-cd/util/log"
"github.com/argoproj/argo-cd/util/lua"
"github.com/argoproj/argo-cd/util/rand"
)
var syncIdPrefix uint64 = 0
const (
// EnvVarSyncWaveDelay is an environment variable which controls the delay in seconds between
// each sync-wave
EnvVarSyncWaveDelay = "ARGOCD_SYNC_WAVE_DELAY"
)
func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha1.OperationState) {
// Sync requests might be requested with ambiguous revisions (e.g. master, HEAD, v1.2.3).
// This can change meaning when resuming operations (e.g a hook sync). After calculating a
@@ -68,7 +78,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
revision = syncOp.Revision
}
proj, err := argo.GetAppProject(&app.Spec, listersv1alpha1.NewAppProjectLister(m.projInformer.GetIndexer()), m.namespace)
proj, err := argo.GetAppProject(&app.Spec, listersv1alpha1.NewAppProjectLister(m.projInformer.GetIndexer()), m.namespace, m.settingsMgr)
if err != nil {
state.Phase = common.OperationError
state.Message = fmt.Sprintf("Failed to load application project: %v", err)
@@ -125,7 +135,14 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
Order: i + 1,
})
}
syncCtx, err := sync.NewSyncContext(compareResult.syncStatus.Revision, compareResult.reconciliationResult, restConfig, rawConfig, m.kubectl, app.Spec.Destination.Namespace, logEntry,
syncCtx, err := sync.NewSyncContext(
compareResult.syncStatus.Revision,
compareResult.reconciliationResult,
restConfig,
rawConfig,
m.kubectl,
app.Spec.Destination.Namespace,
sync.WithLogr(logutils.NewLogrusLogger(logEntry)),
sync.WithHealthOverride(lua.ResourceHealthOverrides(resourceOverrides)),
sync.WithPermissionValidator(func(un *unstructured.Unstructured, res *v1.APIResource) error {
if !proj.IsGroupKindPermitted(un.GroupVersionKind().GroupKind(), res.Namespaced) {
@@ -137,12 +154,19 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
return nil
}),
sync.WithOperationSettings(syncOp.DryRun, syncOp.Prune, syncOp.SyncStrategy.Force(), syncOp.IsApplyStrategy() || len(syncOp.Resources) > 0),
sync.WithInitialState(state.Phase, state.Message, initialResourcesRes),
sync.WithInitialState(state.Phase, state.Message, initialResourcesRes, state.StartedAt),
sync.WithResourcesFilter(func(key kube.ResourceKey, target *unstructured.Unstructured, live *unstructured.Unstructured) bool {
return len(syncOp.Resources) == 0 || argo.ContainsSyncResource(key.Name, key.Namespace, schema.GroupVersionKind{Kind: key.Kind, Group: key.Group}, syncOp.Resources)
}),
sync.WithManifestValidation(!syncOp.SyncOptions.HasOption("Validate=false")),
sync.WithNamespaceCreation(syncOp.SyncOptions.HasOption("CreateNamespace=true")),
sync.WithNamespaceCreation(syncOp.SyncOptions.HasOption("CreateNamespace=true"), func(un *unstructured.Unstructured) bool {
if un != nil && kube.GetAppInstanceLabel(un, cdcommon.LabelKeyAppInstance) != "" {
kube.UnsetLabel(un, cdcommon.LabelKeyAppInstance)
return true
}
return false
}),
sync.WithSyncWaveHook(delayBetweenSyncWaves),
)
if err != nil {
@@ -185,3 +209,25 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
}
}
}
// delayBetweenSyncWaves is a gitops-engine SyncWaveHook which introduces an artificial delay
// between each sync wave. We introduce an artificial delay in order give other controllers a
// _chance_ to react to the spec change that we just applied. This is important because without
// this, Argo CD will likely assess resource health too quickly (against the stale object), causing
// hooks to fire prematurely. See: https://github.com/argoproj/argo-cd/issues/4669.
// Note, this is not foolproof, since a proper fix would require the CRD record
// status.observedGeneration coupled with a health.lua that verifies
// status.observedGeneration == metadata.generation
func delayBetweenSyncWaves(phase common.SyncPhase, wave int, finalWave bool) error {
if !finalWave {
delaySec := 2
if delaySecStr := os.Getenv(EnvVarSyncWaveDelay); delaySecStr != "" {
if val, err := strconv.Atoi(delaySecStr); err == nil {
delaySec = val
}
}
duration := time.Duration(delaySec) * time.Second
time.Sleep(duration)
}
return nil
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

175
docs/assets/versions.css Normal file
View File

@@ -0,0 +1,175 @@
.md-header__title {
display: flex;
}
.dropdown-caret {
display: inline-block !important;
position: absolute;
right: 4px;
}
.fa .fa-caret-down {
display: none !important;
}
.rst-other-versions {
text-align: right;
}
.rst-other-versions > dl, .rst-other-versions dt, .rst-other-versions small {
display: none;
}
.rst-other-versions > dl:first-child {
display: flex !important;
flex-direction: column;
line-height: 0px !important;
}
.rst-versions.shift-up .rst-other-versions {
display: flex !important;
}
.rst-versions .rst-other-versions {
display: none;
}
/* Version Warning */
div[data-md-component=announce] {
background-color: rgb(248, 243, 236);
position: sticky;
top: 0;
z-index: 2;
}
div[data-md-component=announce]>div#announce-msg{
color: var(--md-code-hl-number-color);
font-size: .8rem;
text-align: center;
margin: 15px;
}
div[data-md-component=announce]>div#announce-msg>a{
color: var(--md-typeset-a-color);
text-decoration: underline;
}
/* from https://assets.readthedocs.org/static/css/badge_only.css,
most styles have to be overriden here */
.rst-versions{
position: relative !important;
bottom: 0;
left: 0;
width: 100px !important;
background: hsla(173, 100%, 24%, 1) !important;
font-family: inherit !important;
z-index: 0 !important;
}
.rst-versions a{
color:#2980B9;
text-decoration:none
}
.rst-versions .rst-badge-small{
display:none
}
.rst-versions .rst-current-version{
padding:12px;
background: hsla(173, 100%, 24%, 1) !important;
display:block;
text-align:right;
font-size:90%;
cursor:pointer;
color: white !important;
*zoom:1
}
.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{
display:table;content:""
}
.rst-versions .rst-current-version:after{
clear:both
}
.rst-versions .rst-current-version .fa{
color:#fcfcfc
}
.rst-versions .rst-current-version .fa-caret-down{
display: none;
}
.rst-versions.shift-up .rst-other-versions{
display:block
}
.rst-versions .rst-other-versions{
font-size:90%;
padding:12px;
color:gray;
display:none
}
.rst-versions .rst-other-versions hr{
display: none !important;
height: 0px !important;
border: 0px;
margin: 0px !important;
padding: 0px;
border-top: none !important;
}
.rst-versions .rst-other-versions dd{
display:inline-block;
margin:0
}
.rst-versions .rst-other-versions dd a{
display:inline-block;
padding: 1em 0em !important;
color:#fcfcfc;
font-size: .6rem !important;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
width: 80px;
}
.rst-versions .rst-other-versions dd a:hover{
font-size: .7rem !important;
font-weight: bold;
}
.rst-versions.rst-badge{
display: block !important;
width: 100px !important;
bottom: 0px !important;
right: 0px !important;
left:auto;
border:none;
text-align: center !important;
line-height: 0;
}
.rst-versions.rst-badge .icon-book{
display: none;
}
.rst-versions.rst-badge .fa-book{
display: none !important;
}
.rst-versions.rst-badge.shift-up .rst-current-version{
text-align: left !important;
}
.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{
display: none !important;
}
.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{
display: none !important;
}
.rst-versions.rst-badge .rst-current-version{
width: 70px !important;
height: 2.4rem !important;
line-height:2.4rem !important;
padding: 0px 5px !important;
display: inline-block !important;
font-size: .6rem !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
text-align: left !important;
}
@media screen and (max-width: 768px){
.rst-versions{
width:85%;
display:none
}
.rst-versions.shift{
display:block
}
}

58
docs/assets/versions.js Normal file
View File

@@ -0,0 +1,58 @@
setTimeout(function() {
const callbackName = 'callback_' + new Date().getTime();
window[callbackName] = function (response) {
const div = document.createElement('div');
div.innerHTML = response.html;
document.querySelector(".md-header__inner > .md-header__title").appendChild(div);
const container = div.querySelector('.rst-versions');
var caret = document.createElement('div');
caret.innerHTML = "<i class='fa fa-caret-down dropdown-caret'></i>"
caret.classList.add('dropdown-caret')
div.querySelector('.rst-current-version').appendChild(caret);
div.querySelector('.rst-current-version').addEventListener('click', function() {
const classes = container.className.split(' ');
const index = classes.indexOf('shift-up');
if (index === -1) {
classes.push('shift-up');
} else {
classes.splice(index, 1);
}
container.className = classes.join(' ');
});
}
var CSSLink = document.createElement('link');
CSSLink.rel='stylesheet';
CSSLink.href = '/assets/versions.css';
document.getElementsByTagName('head')[0].appendChild(CSSLink);
var script = document.createElement('script');
script.src = 'https://argo-cd.readthedocs.io/_/api/v2/footer_html/?'+
'callback=' + callbackName + '&project=argo-cd&page=&theme=mkdocs&format=jsonp&docroot=docs&source_suffix=.md&version=' + (window['READTHEDOCS_DATA'] || { version: 'latest' }).version;
document.getElementsByTagName('head')[0].appendChild(script);
}, 0);
// VERSION WARNINGS
window.addEventListener("DOMContentLoaded", function() {
var rtdData = window['READTHEDOCS_DATA'] || { version: 'latest' };
var margin = 30;
var headerHeight = document.getElementsByClassName("md-header")[0].offsetHeight;
if (rtdData.version === "latest") {
document.querySelector("div[data-md-component=announce]").innerHTML = "<div id='announce-msg'>You are viewing the docs for an unreleased version of Argo CD, <a href='https://argo-cd.readthedocs.io/en/stable/'>click here to go to the latest stable version.</a></div>"
var bannerHeight = document.getElementById('announce-msg').offsetHeight + margin
document.querySelector("header.md-header").style.top = bannerHeight +"px";
document.querySelector('style').textContent +=
"@media screen and (min-width: 76.25em){ .md-sidebar { height: 0; top:"+ (bannerHeight+headerHeight)+"px !important; }}"
document.querySelector('style').textContent +=
"@media screen and (min-width: 60em){ .md-sidebar--secondary { height: 0; top:"+ (bannerHeight+headerHeight)+"px !important; }}"
}
else if ((window['READTHEDOCS_DATA']).version !== "stable") {
document.querySelector("div[data-md-component=announce]").innerHTML = "<div id='announce-msg'>You are viewing the docs for a previous version of Argo CD, <a href='https://argo-cd.readthedocs.io/en/stable/'>click here to go to the latest stable version.</a></div>"
var bannerHeight = document.getElementById('announce-msg').offsetHeight + margin
document.querySelector("header.md-header").style.top = bannerHeight +"px";
document.querySelector('style').textContent +=
"@media screen and (min-width: 76.25em){ .md-sidebar { height: 0; top:"+ (bannerHeight+headerHeight)+"px !important; }}"
document.querySelector('style').textContent +=
"@media screen and (min-width: 60em){ .md-sidebar--secondary { height: 0; top:"+ (bannerHeight+headerHeight)+"px !important; }}"
}
});

View File

@@ -29,8 +29,7 @@ You should now be able to run `argocd` commands.
### Homebrew
```bash
brew tap argoproj/tap
brew install argoproj/tap/argocd
brew install argocd
```
### Download With Curl
@@ -54,3 +53,27 @@ chmod +x /usr/local/bin/argocd
```
After finishing either of the instructions above, you should now be able to run `argocd` commands.
## Windows
### Download With Powershell: Invoke-WebRequest
You can view the latest version of Argo CD at the link above or run the following command to grab the version:
```powershell
$version = (Invoke-RestMethod https://api.github.com/repos/argoproj/argo-cd/releases/latest).tag_name
```
Replace `$version` in the command below with the version of Argo CD you would like to download:
```powershell
$url = "https://github.com/argoproj/argo-cd/releases/download/" + $version + "/argocd-windows-amd64.exe"
$output = "argocd.exe"
Invoke-WebRequest -Uri $url -OutFile $output
```
Also please note you will probably need to move the file into your PATH.
After finishing the instructions above, you should now be able to run `argocd` commands.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 109 KiB

View File

@@ -1,30 +1,51 @@
# CI
# Continuous Integration (CI)
!!!warning
This documentation is out-of-date. Please bear with us while we work to
update the documentation to reflect reality!
## Troubleshooting CI checks
## Troubleshooting Builds
You can click on the "Details" link next to the failed step to get more information about the failure.
### "Check nothing has changed" step fails
If your PR fails the `codegen` CI step, you can either:
![Failed GitHub Action](ci-pipeline-failed.png)
(1) Simple - download the `codgen.patch` file from CircleCI and apply it:
To read more about The GitHub actions are configured in [`ci-build.yaml`](https://github.com/argoproj/argo-cd/blob/master/.github/workflows/ci-build.yaml).
![download codegen patch file](../assets/download-codegen-patch-file.png)
### Can I retrigger the checks without pushing a new commit?
Since the CI pipeline is triggered on Git commits, there is currently no (known) way on how to retrigger the CI checks without pushing a new commit to your branch.
If you are absolutely sure that the failure was due to a failure in the pipeline, and not an error within the changes you commited, you can push an empty commit to your branch, thus retriggering the pipeline without any code changes. To do so, issue
```bash
git apply codegen.patch
git commit -am "Applies codegen patch"
git commit --allow-empty -m "Retrigger CI pipeline"
git push origin <yourbranch>
```
(2) Advanced - if you have the tools installed (see the contributing guide), run the following:
### Why does the build step fail?
```bash
make pre-commit
git commit -am 'Ran pre-commit checks'
```
First, make sure the failing build step succeeds on your machine. Remember the containerized build toolchain is available, too.
If the build is failing at the `Ensuring Gopkg.lock is up-to-date` step, you need to update the dependencies before you push your commits. Run `make dep-ensure` and `make dep` and commit the changes to `Gopkg.lock` to your branch.
### Why does the codegen step fail?
If the codegen step fails with "Check nothing has changed...", chances are high that you did not run `make codegen`, or did not commit the changes it made. You should double check by running `make codegen` followed by `git status` in the local working copy of your branch. Commit any changes and push them to your GH branch to have the CI check it again.
A second common case for this is, when you modified any of the auto generated assets, as these will be overwritten upon `make codegen`.
Generally, this step runs `codegen` and compares the outcome against the Git branch it has checked out. If there are differences, the step will fail.
See [What checked-in code is generated and where does it come from?](faq.md#what-checked-in-code-is-generated-and-how-is-it-generated) for more information.
### Why does the lint step fail?
Your code failed to lint correctly, or modifications were performed by the `golangci-lint` process.
* You should run `make lint`, or `golangci-lint run` on your local branch and fix all the issues.
* If you receive an error like, ```File is not `goimports`-ed (goimports)```, the file is not formatted correctly. Run `gofmt -w $file.go` to resolve this linter error.
### Why does the test or e2e steps fail?
You should check for the cause of the failure in the check's detail page as described above. This will give you the name of the test that has failed, and details about why. If your test are passing locally (using the virtualized toolchain), chances are that the test might be flaky and will pass the next time it is run. Please retrigger the CI pipeline as described above and see if the test step now passes.
## Updating The Builder Image

View File

@@ -6,7 +6,7 @@ We want to make contributing to ArgoCD as simple and smooth as possible.
This guide shall help you in setting up your build & test environment, so that you can start developing and testing bug fixes and feature enhancements without having to make too much effort in setting up a local toolchain.
If you want to to submit a PR, please read this document carefully, as it contains important information guiding you through our PR quality gates.
If you want to submit a PR, please read this document carefully, as it contains important information guiding you through our PR quality gates.
As is the case with the development process, this document is under constant change. If you notice any error, or if you think this document is out-of-date, or if you think it is missing something: Feel free to submit a PR or submit a bug to our GitHub issue tracker.
@@ -76,7 +76,7 @@ After you have submitted your PR, and whenever you push new commits to that bran
* Run a Go linter on the code (`make lint`)
* Run the unit tests (`make test`)
* Run the End-to-End tests (`make test-e2e`)
* Build and lint the UI code (`make ui`)
* Build and lint the UI code (`make lint-ui`)
* Build the `argocd` CLI (`make cli`)
If any of these tests in the CI pipeline fail, it means that some of your contribution is considered faulty (or a test might be flaky, see below).
@@ -156,6 +156,36 @@ make: *** [Makefile:386: verify-kube-connect] Error 1
you should edit your `~/.kube/config` and modify the `server` option to point to your correct K8s API (as described above).
### Using k3d
[k3d](https://github.com/rancher/k3d) is a lightweight wrapper to run [k3s](https://github.com/rancher/k3s), a minimal Kubernetes distribution, in docker. Because it's running in a docker container, you're dealing with docker's internal networking rules when using k3d. A typical Kubernetes cluster running on your local machine is part of the same network that you're on so you can access it using **kubectl**. However, a Kubernetes cluster running within a docker container (in this case, the one launched by make) cannot access 0.0.0.0 from inside the container itself, when 0.0.0.0 is a network resource outside the container itself (and/or the container's network). This is the cost of a fully self-contained, disposable Kubernetes cluster. The following steps should help with a successful `make verify-kube-connect` execution.
1. Find your host IP by executing `ifconfig` on Mac/Linux and `ipconfig` on Windows. For most users, the following command works to find the IP address.
For Mac:
```
IP=`ifconfig en0 | grep inet | grep -v inet6 | awk '{print $2}'`
echo $IP
```
For Linux:
```
IP=`ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2}'`
echo $IP
```
Keep in mind that this IP is dynamically assigned by the router so if your router restarts for any reason, your IP might change.
2. Edit your ~/.kube/config and replace 0.0.0.0 with the above IP address.
3. Execute a `kubectl version` to make sure you can still connect to the Kubernetes API server via this new IP. Run `make verify-kube-connect` and check if it works.
4. Finally, so that you don't have to keep updating your kube-config whenever you spin up a new k3d cluster, add `--api-port $IP:6550` to your **k3d cluster create** command, where $IP is the value from step 1. An example command is provided here.
```
k3d cluster create my-cluster --wait --k3s-server-arg '--disable=traefik' --api-port $IP:6550 -p 443:443@loadbalancer
```
## The development cycle
When you have developed and possibly manually tested the code you want to contribute, you should ensure that everything will build correctly. Commit your changes to the local copy of your Git branch and perform the following steps:

View File

@@ -0,0 +1,57 @@
# Debugging a Remote ArgoCD Environment
In this guide, we will describe how to debug a remote ArgoCD environment with [Telepresence](https://telepresence.io/).
Telepresence allows you to connect & debug a service deployed in a remote environment and to "cherry-pick" one service to run locally, staying connected to the remote cluster. This will:
* Reduce resource footprint on the local machine
* Decrease the feedback loop time
* Result in more confidence about the delivered code.
To read more about it, refer to the official documentation at [telepresence.io](https://telepresence.io/) or [Medium](https://medium.com/containers-101/development-environment-using-telepresence-634bd7210c26).
## Install ArgoCD
First of all, install ArgoCD on your cluster
```shell
kubectl create ns argocd
curl -sSfL https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml | kubectl apply -n argocd -f -
```
## Connect
Connect to one of the services, for example, to debug the main ArgoCD server run:
```shell
telepresence --swap-deployment argocd-server --namespace argocd --env-file .envrc.remote --expose 8080:8080 --expose 8083:8083 --run bash
```
* `--swap-deployment` changes the argocd-server deployment
* `--expose` forwards traffic of remote ports 8080 and 8083 to the same ports locally
* `--env-file` writes all the environment variables of the remote pod into a local file, the variables are also set on the subprocess of the `--run` command
* `--run` defines which command to run once a connection is established, use `bash`, `zsh` or others
## Debug
Once a connection is established, use your favorite tools to start the server locally.
### Terminal
* Compile `make server`
* Run `./dist/argocd-server`
### VSCode
In VSCode use the integrated terminal to run the Telepresence command to connect. Then, to run argocd-server service use the following configuration.
Make sure to run `packr` before starting the debugging session to generate the assets.
Update the configuration file to point to kubeconfig file: `KUBECONFIG=` (required)
```json
{
"name": "Launch",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/cmd/argocd-server",
"envFile": [
"${workspaceFolder}/.envrc.remote",
],
"env": {
"CGO_ENABLED": "0",
"KUBECONFIG": "/path/to/kube/config"
}
}
```

View File

@@ -0,0 +1,54 @@
# Managing Dependencies
## GitOps Engine (`github.com/argoproj/gitops-engine`)
### Repository
https://github.com/argoproj/gitops-engine
### Pulling changes from `gitops-engine`
After your GitOps Engine PR has been merged, ArgoCD needs to be updated to pull in the version of the GitOps engine that contains your change. Here are the steps:
* Retrieve the SHA hash for your commit. You will use this in the next step.
* From the `argo-cd` folder, run the following command
`go get github.com/argoproj/gitops-engine@<git-commit-sha>`
If you get an error message `invalid version: unknown revision` then you got the wrong SHA hash
* Run:
`go mod tidy`
* The following files are changed:
- `go.mod`
- `go.sum`
* Create an ArgoCD PR with a `refactor:` type in its title for the two file changes.
### Tips:
* See https://github.com/argoproj/argo-cd/pull/4434 as an example
* The PR might require additional, dependent changes in ArgoCD that are directly impacted by the changes made in the engine.
## Argo UI Components
### Repository
https://github.com/argoproj/argo-ui
### Pulling changes from Argo UI into Argo CD
If you make changes to the Argo UI component, and your Argo CD changes depend on those changes, follow these steps:
1. Make changes to Argo UI and submit the PR request.
2. Also, prepare your Argo CD changes, but don't create the PR just yet.
3. **After** the Argo UI PR has been merged to master, then as part of your Argo CD changes:
- Run `yarn add https://github.com/argoproj/argo-ui.git`, and then,
- Check in the regenerated yarn.lock file as part of your Argo CD commit
4. Create the Argo CD PR when you are ready. The PR build and test checks should pass.
If your Argo UI change is a 'stand-alone' fix, and you simply want Argo CD to pull in your change, then simply create an Argo CD PR with the yarn.lock file change.

View File

@@ -6,9 +6,9 @@
Sure thing! You can either open an Enhancement Proposal in our GitHub issue tracker or you can [join us on Slack](https://argoproj.github.io/community/join-slack) in channel #argo-dev to discuss your ideas and get guidance for submitting a PR.
### Noone has looked at my PR yet. Why?
### No one has looked at my PR yet. Why?
As we have limited man power, it can sometimes take a while for someone to respond to your PR. Especially, when your PR contains complex or non-obvious changes. Please bear with us, we try to look at every PR that we receive.
As we have limited manpower, it can sometimes take a while for someone to respond to your PR. Especially, when your PR contains complex or non-obvious changes. Please bear with us, we try to look at every PR that we receive.
### Why has my PR been declined? I put much work in it!
@@ -16,50 +16,17 @@ We appreciate that you have put your valuable time and know how into a contribut
To be on the safe side, make sure that you have created an Enhancement Proposal for your change before starting to work on your PR and have gathered enough feedback from the community and the maintainers.
## Failing CI checks
### A check on my PR is failing.
See [Failing CI Checks](ci.md#troubleshooting-ci-checks).
### One of the CI checks failed. Why?
### What checked-in code is generated, and how is it generated?
The following files under this repository are generated, and must be kept up-to-date. Also see [Why does the codegen step fail?](ci.md#why-does-the-codegen-step-fail).
You can click on the "Details" link next to the failed step to get more details about the failure. This will take you to CircleCI website.
See the Makefile for targets that can also run these scripts, and the `codegen` target which runs them all.
![CircleCI pipeline](ci-pipeline-failed.png)
### Can I retrigger the checks without pushing a new commit?
Since the CI pipeline is triggered on Git commits, there is currently no (known) way on how to retrigger the CI checks without pushing a new commit to your branch.
If you are absolutely sure that the failure was due to a failure in the pipeline, and not an error within the changes you commited, you can push an empty commit to your branch, thus retriggering the pipeline without any code changes. To do so, issue
```bash
git commit --allow-empty -m "Retrigger CI pipeline"
git push origin <yourbranch>
```
### Why does the build step fail?
Chances are that it fails for two of the following reasons in the CI while running fine on your machine:
* Sometimes, CircleCI kills the build step due to excessive memory usage. This happens rarely, but it has happened in the past. If you see a message like "killed" in the log output of CircleCI, you should retrigger the pipeline as described above. If the issue persists, please let us know.
* If the build is failing at the `Ensuring Gopkg.lock is up-to-date` step, you need to update the dependencies before you push your commits. Run `make dep-ensure` and `make dep` and commit the changes to `Gopkg.lock` to your branch.
### Why does the codegen step fail?
If the codegen step fails with "Check nothing has changed...", chances are high that you did not run `make codegen`, or did not commit the changes it made. You should double check by running `make codegen` followed by `git status` in the local working copy of your branch. Commit any changes and push them to your GH branch to have the CI check it again.
A second common case for this is, when you modified any of the auto generated assets, as these will be overwritten upon `make codegen`.
Generally, this step runs `codegen` and compares the outcome against the Git branch it has checked out. If there are differences, the step will fail.
### Why does the lint step fail?
The lint step is most likely to fail for two reasons:
* The `golangci-lint` process was OOM killed by CircleCI. This happens sometimes, and is annoying. This is indicated by a `Killed.` message in the CircleCI output.
If this is the case, please re-trigger the CI process as described above and see if it runs through.
* Your code failed to lint correctly, or modifications were performed by the `golangci-lint` process. You should run `make lint` on your local branch and fix all the issues.
### Why does the test or e2e steps fail?
You should check for the cause of the failure on the CircleCI web site, as described above. This will give you the name of the test that has failed, and details about why. If your test are passing locally (using the virtualized toolchain), chances are that the test might be flaky and will pass the next time it is run. Please retrigger the CI pipeline as described above and see if the test step now passes.
| Filename | Purpose | Generated by |
| -------- | ------- | ------------ |
| `*.pb.go`, `*.pb.gw.go` | [Protobuf](https://developers.google.com/protocol-buffers/docs/gotutorial) Interfaces | `hack/generate-proto.sh` |
| `assets/swagger.json` | Swagger 2 API spec | `hack/update-openapi.sh` |
| `manifests/` | k8s Installation Manifests | `hack/update-manifests.sh` |
| `docs/user-guide/commands` | CLI Documentation | `tools/cmd-docs/main.go` |

View File

@@ -2,19 +2,19 @@
## Automated release procedure
Starting from `release-1.6` branch, ArgoCD can be released in automatic fashion
Starting from `release-1.6` branch, ArgoCD can be released in an automated fashion
using GitHub actions. The release process takes about 20 minutes, sometimes a
little less, depending on the performance of GitHub actions runners.
little less, depending on the performance of GitHub Actions runners.
The target release branch must already exist in GitHub repository. If you for
The target release branch must already exist in the GitHub repository. If you for
example want to create a release `v1.7.0`, the corresponding release branch
`release-1.7` needs to exist, otherwise the release cannot be build. Also,
`release-1.7` needs to exist, otherwise, the release cannot be built. Also,
the trigger tag should always be created in the release branch, checked out
in your local repository clone.
Before triggering the release automation, the `CHANGELOG.md` should be updated
with the latest information, and this change should be commited and pushed to
the GitHub repository to the release branch. Afterwards, the automation can be
with the latest information, and this change should be committed and pushed to
the GitHub repository to the release branch. Afterward, the automation can be
triggered.
**Manual steps before release creation:**
@@ -25,13 +25,13 @@ triggered.
**The automation will perform the following steps:**
* Update `VERSION` file in release branch
* Update manifests with image tags of new version in release branch
* Update `VERSION` file in the release branch
* Update manifests with image tags of the new version in the release branch
* Build the Docker image and push to Docker Hub
* Create release tag in the GitHub repository
* Create GitHub release and attach the required assets to it (CLI binaries, ...)
* Create a release tag in the GitHub repository
* Create a GitHub release and attach the required assets to it (CLI binaries, ...)
Finally, it will the remove trigger tag from repository again.
Finally, it will the remove trigger tag from the repository again.
Automation supports both, GA and pre-releases. The automation is triggered by
pushing a tag to the repository. The tag must be in one of the following formats
@@ -42,10 +42,10 @@ to trigger the GH workflow:
The tag must be an annotated tag, and it must contain the release notes in the
commit message. Please note that Markdown uses `#` character for formatting, but
Git uses it as comment char. To solve this, temporarily switch Git comment char
Git uses it as comment char. To solve this, temporarily switch Git's comment char
to something else, the `;` character is recommended.
For example, considering you have configured the Git remote for repository to
For example, consider you have configured the Git remote for the repository to
`github.com/argoproj/argo-cd` to be named `upstream` and are in your locally
checked out repo:
@@ -65,7 +65,7 @@ it to the GitHub repo.
In summary, the modifications it does are:
* Create annotated trigger tag in your local repository
* Push tag to GitHub repository to trigger workflow
* Push the tag to the GitHub repository to trigger the workflow
* Remove trigger tag from your local repository
The script can be found at `hacks/trigger-release.sh` and is used as follows:
@@ -79,35 +79,35 @@ prefix, so just specify it as `v1.6.0-rc2` for example. The `<remote name>`
specifies the name of the remote used to push to the GitHub repository.
If you omit the `<release notes path>`, an editor will pop-up asking you to
enter the tag's annotation so you can paste the release notes, save and exit.
enter the tag's annotation so you can paste the release notes, save, and exit.
It will also take care of temporarily configuring the `core.commentChar` and
setting it back to its original state.
!!!note
:warning:
It is strongly recommended to use this script to trigger the workflow
instead of manually pushing a tag to the repository.
Once the trigger tag is pushed to the repo, the GitHub workflow will start
execution. You can follow its progress under `Actions` tab, the name of the
execution. You can follow its progress under the `Actions` tab, the name of the
action is `Create release`. Don't get confused by the name of the running
workflow, it will be the commit message of the latest commit to `master`
workflow, it will be the commit message of the latest commit to the `master`
branch, this is a limitation of GH actions.
The workflow performs necessary checks so that the release can be sucessfully
build before the build actually starts. It will error when one of the
prerequisites is not met, or if the release cannot be build (i.e. already
The workflow performs necessary checks so that the release can be successfully
built before the build actually starts. It will error when one of the
prerequisites is not met, or if the release cannot be built (i.e. already
exists, release notes invalid, etc etc). You can see a summary of what has
failed in the job's overview page, and more detailed errors in the output
failed in the job's overview page and more detailed errors in the output
of the step that has failed.
!!!note
:warning:
You cannot perform more than one release on the same release branch at the
same time. For example, both `v1.6.0` and `v1.6.1` would operate on the
`release-1.6` branch. If you submit `v1.6.1` while `v1.6.0` is still
executing, the release automation will not execute. You have to either
cancel `v1.6.0` before submitting `v1.6.1` or wait until it has finished.
You can execute releases on different release branches simultaneously, for
example `v1.6.0` and `v1.7.0-rc1`, without problems.
example, `v1.6.0` and `v1.7.0-rc1`, without problems.
### Verifying automated release
@@ -116,7 +116,7 @@ checks to see if the release came out correctly:
* Check status & output of the GitHub action
* Check [https://github.com/argoproj/argo-cd/releases](https://github.com/argoproj/argo-cd/releases)
to see if release has been correctly created, and if all required assets
to see if the release has been correctly created and if all required assets
are attached.
* Check whether the image has been published on DockerHub correctly
@@ -125,12 +125,12 @@ checks to see if the release came out correctly:
If something went wrong, damage should be limited. Depending on the steps that
have been performed, you will need to manually clean up.
* Delete release tag (i.e. `v1.6.0-rc2`) created on GitHub repository. This
will immediately set release (if created) to `draft` status, invisible for
* Delete the release tag (e.g. `v1.6.0-rc2`) created in the GitHub repository. This
will immediately set the release (if created) to `draft` status, invisible to the
general public.
* Delete the draft release (if created) from `Releases` page on GitHub
* Delete the draft release (if created) from the `Releases` page on GitHub
* If Docker image has been pushed to DockerHub, delete it
* If commits have been performed to the release branch, revert them. Paths that could have been commited to are:
* If commits have been performed to the release branch, revert them. Paths that could have been committed to are:
* `VERSION`
* `manifests/*`
@@ -138,14 +138,14 @@ have been performed, you will need to manually clean up.
For now, the only manual steps left are to
* update brew formulae for ArgoCD CLI on Mac if release is GA
* update stable tag in GitHub repository to point to new release (if appropriate)
* update stable tag in the GitHub repository to point to new the release (if appropriate)
* update the `VERSION` file on `master` if this is a new major release
These will be automated as well in the future.
These may be automated as well in the future.
## Manual releasing
Automatic release process does not interfere with manual release process, since
The automatic release process does not interfere with the manual release process, since
the trigger tag does not match a normal release tag. If you prefer to perform,
manual release or if automatic release is for some reason broken, these are the
steps:
@@ -172,7 +172,7 @@ VERSION=v1.3.0-rc1
VERSION=v1.3.1
```
Update `VERSION` and manifests with new version:
Update `VERSION` and manifests with the new version:
```bash
git checkout $BRANCH
@@ -192,22 +192,18 @@ git push $REPO $BRANCH
git push $REPO $VERSION
```
Update [Github releases](https://github.com/argoproj/argo-cd/releases) with:
Update [GitHub releases](https://github.com/argoproj/argo-cd/releases) with:
* Getting started (copy from previous release)
* Getting started (copy from the previous release)
* Changelog
* Binaries (e.g. `dist/argocd-darwin-amd64`).
## Update brew formulae (manual)
If GA, update Brew formula:
If GA, update the Brew formula:
```bash
git clone git@github.com:argoproj/homebrew-tap.git
cd homebrew-tap
./update.sh argocd $VERSION
git commit -am "Update argocd to $VERSION"
git push
brew bump-formula-pr argocd --version ${VERSION:1}
```
## Update stable tag (manual)

View File

@@ -22,7 +22,7 @@ kubectl apply -n argocd --force -f manifests/install.yaml
Make sure that ArgoCD is not running in your development cluster by scaling down the deployments:
```shell
kubectl -n argocd scale deployment/argocd-application-controller --replicas 0
kubectl -n argocd scale statefulset/argocd-application-controller --replicas 0
kubectl -n argocd scale deployment/argocd-dex-server --replicas 0
kubectl -n argocd scale deployment/argocd-repo-server --replicas 0
kubectl -n argocd scale deployment/argocd-server --replicas 0
@@ -31,7 +31,7 @@ kubectl -n argocd scale deployment/argocd-redis --replicas 0
### Start local services
When you use the virtualized toolchain, starting local services is as simple as running
Before starting local services, make sure you are present in `argocd` namespace. When you use the virtualized toolchain, starting local services is as simple as running
```bash
make start
@@ -56,7 +56,7 @@ export ARGOCD_OPTS="--plaintext --insecure"
Once you have finished testing your changes locally and want to bring back ArgoCD in your development cluster, simply scale the deployments up again:
```bash
kubectl -n argocd scale deployment/argocd-application-controller --replicas 1
kubectl -n argocd scale statefulset/argocd-application-controller --replicas 1
kubectl -n argocd scale deployment/argocd-dex-server --replicas 1
kubectl -n argocd scale deployment/argocd-repo-server --replicas 1
kubectl -n argocd scale deployment/argocd-server --replicas 1

View File

@@ -17,12 +17,6 @@ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/st
This will create a new namespace, `argocd`, where Argo CD services and application resources will live.
On GKE, you will need grant your account the ability to create new cluster roles:
```bash
kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin --user="$(gcloud config get-value account)"
```
!!! note
If you are not interested in UI, SSO, multi-cluster management and just want to pull changes into the cluster then you can disable
authentication using `--disable-auth` flag and access Argo CD via CLI using `--port-forward` or `--port-forward-namespace` flags
@@ -37,8 +31,7 @@ Download the latest Argo CD version from [https://github.com/argoproj/argo-cd/re
Also available in Mac Homebrew:
```bash
brew tap argoproj/tap
brew install argoproj/tap/argocd
brew install argocd
```
## 3. Access The Argo CD API Server
@@ -87,6 +80,10 @@ Change the password using the command:
argocd account update-password
```
!!! note
The initial password is set in a kubernetes secret, named `argocd-secret`, during ArgoCD's initial start up. This means if you edit
the deployment in any way which causes a new pod to be deployed, such as disabling TLS on the Argo CD API server. Take note of the initial
pod name when you first install Argo CD, or reset the password by following [these instructions](https://argoproj.github.io/argo-cd/faq/#i-forgot-the-admin-password-how-do-i-reset-it)
## 5. Register A Cluster To Deploy Apps To (Optional)
@@ -94,15 +91,15 @@ This step registers a cluster's credentials to Argo CD, and is only necessary wh
an external cluster. When deploying internally (to the same cluster that Argo CD is running in),
https://kubernetes.default.svc should be used as the application's K8s API server address.
First list all clusters contexts in your current kubconfig:
First list all clusters contexts in your current kubeconfig:
```bash
argocd cluster add
```
Choose a context name from the list and supply it to `argocd cluster add CONTEXTNAME`. For example,
for docker-for-desktop context, run:
for docker-desktop context, run:
```bash
argocd cluster add docker-for-desktop
argocd cluster add docker-desktop
```
The above command installs a ServiceAccount (`argocd-manager`), into the kube-system namespace of

View File

@@ -24,7 +24,7 @@ kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
```
Follow our [getting started guide](getting_started.md). Further user oriented [documentation](user_guide/)
Follow our [getting started guide](getting_started.md). Further user oriented [documentation](user-guide/)
is provided for additional features. If you are looking to upgrade ArgoCD, see the [upgrade guide](./operator-manual/upgrading/overview.md).
Developer oriented [documentation](developer-guide/) is available for people interested in building third-party integrations.

View File

@@ -23,6 +23,9 @@ spec:
parameters:
- name: "nginx-ingress.controller.service.annotations.external-dns\\.alpha\\.kubernetes\\.io/hostname"
value: mydomain.example.com
- name: "ingress.annotations.kubernetes\\.io/tls-acme"
value: "true"
forceString: true # ensures that value is treated as a string
# Release name override (defaults to application name)
releaseName: guestbook
@@ -48,6 +51,10 @@ spec:
hosts:
- mydomain.example.com
# Optional Helm version to template with. If omitted it will fallback to look at the 'apiVersion' in Chart.yaml
# and decide which Helm binary to use automatically. This field can be either 'v2' or 'v3'.
version: v2
# kustomize specific config
kustomize:
# Optional kustomize version. Note: version must be configured in argocd-cm ConfigMap
@@ -94,10 +101,12 @@ spec:
automated: # automated sync by default retries failed attempts 5 times with following delays between attempts ( 5s, 10s, 20s, 40s, 80s ); retry controlled using `retry` field.
prune: true # Specifies if resources should be pruned during auto-syncing ( false by default ).
selfHeal: true # Specifies if partial app sync should be executed when resources are changed only in target Kubernetes cluster and no git change detected ( false by default ).
allowEmpty: false # Allows deleting all application resources during automatic syncing ( false by default ).
syncOptions: # Sync options which modifies sync behavior
- Validate=false # disables resource validation (equivalent to 'kubectl apply --validate=true')
- CreateNamespace=true # Namespace Auto-Creation ensures that namespace specified as the application destination exists in the destination cluster.
# The retry feature is available since v1.7
retry:
retry:
limit: 5 # number of failed sync attempt retries; unlimited number of attempts if less than 0
backoff:
duration: 5s # the amount to back off. Default unit is seconds, but could also be a duration (e.g. "2m", "1h")

View File

@@ -5,7 +5,7 @@ Sometimes, it may be desired to customize certain components of the UI for brand
help distinguish between multiple instances of Argo CD running in different environments.
Such custom styling can be applied either by supplying a URL to a remotely hosted CSS file, or by
loading a CSS file directly onto the argocd-server container. Both mechanisms are drievn by modifying
loading a CSS file directly onto the argocd-server container. Both mechanisms are driven by modifying
the argocd-cm configMap.
## Adding Styles Via Remote URL

View File

@@ -51,6 +51,16 @@ See [application.yaml](application.yaml) for additional fields. As long as you h
!!! note
The namespace must match the namespace of your Argo cd, typically this is `argocd`.
!!! note
When creating an application from a Helm repository, the `chart` attribute must be specified instead of the `path` attribute within `spec.source`.
```yaml
spec:
source:
repoURL: https://argoproj.github.io/argo-helm
chart: argo
```
!!! warning
By default, deleting an application will not perform a cascade delete, thereby deleting its resources. You must add the finalizer if you want this behaviour - which you may well not want.
@@ -431,7 +441,7 @@ The secret data must include following fields:
* `name` - cluster name
* `server` - cluster api server url
* `namespaces` - optional list of namespaces which are accessible in that cluster. Cluster level resources would be ignored if namespace list is not empty.
* `namespaces` - optional comma-separated list of namespaces which are accessible in that cluster. Cluster level resources would be ignored if namespace list is not empty.
* `config` - JSON representation of following data structure:
```yaml
@@ -444,6 +454,18 @@ bearerToken: string
awsAuthConfig:
clusterName: string
roleARN: string
# Configure external command to supply client credentials
# See https://godoc.org/k8s.io/client-go/tools/clientcmd/api#ExecConfig
execProviderConfig:
command: string
args: [
string
]
env: {
key: value
}
apiVersion: string
installHint: string
# Transport layer security configuration settings
tlsClientConfig:
# PEM-encoded bytes (typically read from a client certificate file).
@@ -460,6 +482,8 @@ tlsClientConfig:
serverName: string
```
Note that if you specify a command to run under `execProviderConfig`, that command must be available in the ArgoCD image. See [BYOI (Build Your Own Image)](custom_tools.md#byoi-build-your-own-image).
Cluster secret example:
```yaml

View File

@@ -8,7 +8,7 @@ A set HA of manifests are provided for users who wish to run Argo CD in a highly
!!! note
The HA installation will require at least three different nodes due to pod anti-affinity roles in the specs.
## Scaling Up
### argocd-repo-server
@@ -20,8 +20,9 @@ The `argocd-repo-server` is responsible for cloning Git repository, keeping it u
* `argocd-repo-server` fork/exec config management tool to generate manifests. The fork can fail due to lack of memory and limit on the number of OS threads.
The `--parallelismlimit` flag controls how many manifests generations are running concurrently and allows avoiding OOM kills.
* one instance of `argocd-repo-server` executes only one operation on one Git repo concurrently. Increase the number of `argocd-repo-server` replica count if you have a lot of
applications in the same repository.
* the `argocd-repo-server` ensures that repository is in the clean state during the manifest generation using config management tools such as Kustomize, Helm
or custom plugin. As a result Git repositories with multiple applications might be affect repository server performance.
Read [Monorepo Scaling Considerations](#monorepo-scaling-considerations) for more information.
* `argocd-repo-server` clones repository into `/tmp` ( of path specified in `TMPDIR` env variable ). Pod might run out of disk space if have too many repository
or repositories has a lot of files. To avoid this problem mount persistent volume.
@@ -35,13 +36,15 @@ and might fail. To avoid failed syncs use `ARGOCD_GIT_ATTEMPTS_COUNT` environmen
* `argocd_git_request_total` - Number of git requests. The metric provides two tags: `repo` - Git repo URL; `request_type` - `ls-remote` or `fetch`.
* `ARGOCD_ENABLE_GRPC_TIME_HISTOGRAM` (v1.8+) - environment variable that enables collecting RPC performance metrics. Enable it if you need to troubleshoot performance issue. Note: metric is expensive to both query and store!
### argocd-application-controller
**settings:**
The `argocd-application-controller` uses `argocd-repo-server` to get generated manifests and Kubernetes API server to get actual cluster state.
* controller uses two separate queues to process application reconciliation (milliseconds) and app syncing (seconds). Number of queue processors for each queue is controlled by
* each controller replica uses two separate queues to process application reconciliation (milliseconds) and app syncing (seconds). Number of queue processors for each queue is controlled by
`--status-processors` (20 by default) and `--operation-processors` (10 by default) flags. Increase number of processors if your Argo CD instance manages too many applications.
For 1000 application we use 50 for `--status-processors` and 25 for `--operation-processors`
@@ -49,17 +52,40 @@ For 1000 application we use 50 for `--status-processors` and 25 for `--operation
The app reconciliation fails with `Context deadline exceeded` error if manifest generating taking too much time. As workaround increase value of `--repo-server-timeout-seconds` and
consider scaling up `argocd-repo-server` deployment.
* controller uses `kubectl` fork/exec to push changes into the cluster and to convert resource from preferred version into user specified version
* The controller uses `kubectl` fork/exec to push changes into the cluster and to convert resource from preferred version into user specified version
(e.g. Deployment `apps/v1` into `extensions/v1beta1`). Same as config management tool `kubectl` fork/exec might cause pod OOM kill. Use `--kubectl-parallelism-limit` flag to limit
number of allowed concurrent kubectl fork/execs.
* controller uses Kubernetes watch APIs to maintain lightweight Kubernetes cluster cache. This allows to avoid querying Kubernetes during app reconciliation and significantly improve
* The controller uses Kubernetes watch APIs to maintain lightweight Kubernetes cluster cache. This allows to avoid querying Kubernetes during app reconciliation and significantly improve
performance. For performance reasons controller monitors and caches only preferred the version of a resource. During reconciliation, the controller might have to convert cached resource from
preferred version into a version of the resource stored in Git. If `kubectl convert` fails because conversion is not supported than controller fallback to Kubernetes API query which slows down
reconciliation. In this case advice user-preferred resource version in Git.
* The controller polls Git every 3m by default. You can increase this duration using `--app-resync seconds` to reduce polling.
* If the controller is managing too many clusters and uses too much memory then you can shard clusters across multiple
controller replicas. To enable sharding increase the number of replicas in `argocd-application-controller` `StatefulSet`
and repeat number of replicas in `ARGOCD_CONTROLLER_REPLICAS` environment variable. The strategic merge patch below
demonstrates changes required to configure two controller replicas.
```yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: argocd-application-controller
spec:
replicas: 2
template:
spec:
containers:
- name: argocd-application-controller
env:
- name: ARGOCD_CONTROLLER_REPLICAS
value: "2"
```
* `ARGOCD_ENABLE_GRPC_TIME_HISTOGRAM` (v1.8+)- environment variable that enables collecting RPC performance metrics. Enable it if you need to troubleshoot performance issue. Note: metric is expensive to both query and store!
**metrics**
* `argocd_app_reconcile` - reports application reconciliation duration. Can be used to build reconciliation duration heat map to get high-level reconciliation performance picture.
@@ -70,6 +96,99 @@ non-preferred version and causes performance issues.
The `argocd-server` is stateless and probably least likely to cause issues. You might consider increasing number of replicas to 3 or more to ensure there is no downtime during upgrades.
**settings:**
* The `ARGOCD_GRPC_MAX_SIZE_MB` environment variable allows specifying the max size of the server response message in megabytes.
The default value is 200. You might need to increase for an Argo CD instance that manages 3000+ applications.
### argocd-dex-server, argocd-redis
The `argocd-dex-server` uses an in-memory database, and two or more instances would have inconsistent data. `argocd-redis` is pre-configured with the understanding of only three total redis servers/sentinels.
## Monorepo Scaling Considerations
Argo CD repo server maintains one repository clone locally and use it for application manifest generation. If the manifest generation requires to change a file in the local repository clone then only one concurrent manifest generation per server instance is allowed. This limitation might significantly slowdown Argo CD if you have a mono repository with multiple applications (50+).
### Enable Concurrent Processing
Argo CD determines if manifest generation might change local files in the local repository clone based on config management tool and application settings.
If the manifest generation has no side effects then requests are processed in parallel without the performance penalty. Following are known cases that might cause slowness and workarounds:
* **Multiple Helm based applications pointing to the same directory in one Git repository:** ensure that your Helm chart don't have don't have conditional
[dependencies](https://helm.sh/docs/chart_best_practices/dependencies/#conditions-and-tags) and create `.argocd-allow-concurrency` file in chart directory.
* **Multiple Custom plugin based applications:** avoid creating temporal files during manifest generation and and create `.argocd-allow-concurrency` file in app directory.
* **Multiple Kustomize or Ksonnet applications in same repository with [parameter overrides](../user-guide/parameters.md):** sorry, no workaround for now.
### Webhook and Manifest Paths Annotation
Argo CD aggressively caches generated manifests and uses repository commit SHA as a cache key. A new commit to the Git repository invalidates cache for all applications configured in the repository
that again negatively affect mono repositories with multiple applications. You might use [webhooks ⧉](https://github.com/argoproj/argo-cd/tree/master/docs/operator-manual/webhook) and `argocd.argoproj.io/manifest-generate-paths` Application
CRD annotation to solve this problem and improve performance.
The `argocd.argoproj.io/manifest-generate-paths` contains a semicolon-separated list of paths within the Git repository that are used during manifest generation. The webhook compares paths specified in the annotation
with the changed files specified in the webhook payload. If non of the changed files are located in the paths then webhook don't trigger application reconciliation and re-uses previously generated manifests cache for a new commit.
Installations that use a different repo for each app are **not** subject to this behavior and will likely get no benefit from using these annotations.
!!! note
Installations with a large number of apps should also set the `--app-resync` flag in the `argocd-application-controller` process to a larger value to reduce automatic refreshes based on git polling. The exact value is a trade-off between reduced work and app sync in case of a missed webhook event. For most cases `1800` (30m) or `3600` (1h) is a good trade-off.
!!! note
Application manifest paths annotation support depends on the git provider used for the Application. It is currently only supported for GitHub, GitLab, and Gogs based repos
* **Relative path** The annotation might contains relative path. In this case the path is considered relative to the path specified in the application source:
```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: guestbook
namespace: argocd
annotations:
# resolves to the 'guestbook' directory
argocd.argoproj.io/manifest-generate-paths: .
spec:
source:
repoURL: https://github.com/argoproj/argocd-example-apps.git
targetRevision: HEAD
path: guestbook
# ...
```
* **Absolute path** The annotation value might be an absolute path started from '/'. In this case path is considered as an absolute path within the Git repository:
```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: guestbook
annotations:
argocd.argoproj.io/manifest-generate-paths: /guestbook
spec:
source:
repoURL: https://github.com/argoproj/argocd-example-apps.git
targetRevision: HEAD
path: guestbook
# ...
```
* **Multiple paths** It is possible to put multiple paths into the annotation. Paths must be separated with a semicolon (`;`):
```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: guestbook
annotations:
# resolves to 'my-application' and 'shared'
argocd.argoproj.io/manifest-generate-paths: .;../shared
spec:
source:
repoURL: https://github.com/argoproj/argocd-example-apps.git
targetRevision: HEAD
path: my-application
# ...
```

View File

@@ -8,6 +8,64 @@ Both protocols are exposed by the argocd-server service object on the following
There are several ways how Ingress can be configured.
## [Ambassador](https://www.getambassador.io/)
The Ambassador Edge Stack can be used as a Kubernetes ingress controller with [automatic TLS termination](https://www.getambassador.io/docs/latest/topics/running/tls/#host) and routing capabilities for both the CLI and the UI.
The API server should be run with TLS disabled. Edit the `argocd-server` deployment to add the `--insecure` flag to the argocd-server command. Given the `argocd` CLI includes the port number in the request `host` header, 2 Mappings are required.
### Option 1: Mapping CRD for Host-based Routing
```yaml
apiVersion: getambassador.io/v2
kind: Mapping
metadata:
name: argocd-server-ui
namespace: argocd
spec:
host: argocd.example.com
prefix: /
service: argocd-server:443
---
apiVersion: getambassador.io/v2
kind: Mapping
metadata:
name: argocd-server-cli
namespace: argocd
spec:
host: argocd.example.com:443
prefix: /
service: argocd-server:443
```
Login with the `argocd` CLI using the extra `--grpc-web-root-path` flag for gRPC-web.
```shell
argocd login <host>:<port> --grpc-web-root-path /
```
### Option 2: Mapping CRD for Path-based Routing
The API server must be configured to be available under a non-root path (e.g. `/argo-cd`). Edit the `argocd-server` deployment to add the `--rootpath=/argo-cd` flag to the argocd-server command.
```yaml
apiVersion: getambassador.io/v2
kind: Mapping
metadata:
name: argocd-server
namespace: argocd
spec:
prefix: /argo-cd
rewrite: /argo-cd
service: argocd-server:443
```
Login with the `argocd` CLI using the extra `--grpc-web-root-path` flag for non-root paths.
```shell
argocd login <host>:<port> --grpc-web-root-path /argo-cd
```
## [kubernetes/ingress-nginx](https://github.com/kubernetes/ingress-nginx)
### Option 1: SSL-Passthrough
@@ -139,9 +197,9 @@ The API server should then be run with TLS disabled. Edit the `argocd-server` de
spec:
template:
spec:
name: argocd-server
containers:
- command:
- name: argocd-server
command:
- /argocd-server
- --staticassets
- /shared/app
@@ -155,35 +213,43 @@ the API server -- one for gRPC and the other for HTTP/HTTPS. However it allows T
happen at the ingress controller.
## [Traefik (v2.0)](https://docs.traefik.io/)
## [Traefik (v2.2)](https://docs.traefik.io/)
Traefik can be used as an edge router and provide [TLS](https://docs.traefik.io/user-guides/crd-acme/) termination within the same deployment.
Traefik can be used as an edge router and provide [TLS](https://docs.traefik.io/user-guides/grpc/) termination within the same deployment.
It currently has an advantage over NGINX in that it can terminate both TCP and HTTP connections _on the same port_ meaning you do not require multiple ingress objects and hosts.
It currently has an advantage over NGINX in that it can terminate both TCP and HTTP connections _on the same port_ meaning you do not require multiple hosts or paths.
The API server should be run with TLS disabled. Edit the `argocd-server` deployment to add the `--insecure` flag to the argocd-server command.
### IngressRoute CRD
```yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: argocd-server-ingress
name: argocd-server
namespace: argocd
spec:
entryPoints:
- websecure
routes:
- match: Host(`argocd.example.com`)
kind: Rule
- kind: Rule
match: Host(`argocd.example.com`)
priority: 10
services:
- name: argocd-server
port: 80
- kind: Rule
match: Host(`argocd.example.com`) && Headers(`Content-Type`, `application/grpc`)
priority: 11
services:
- name: argocd-server
port: 80
scheme: h2c
tls:
certResolver: default
options: {}
```
## AWS Application Load Balancers (ALBs) And Classic ELB (HTTP Mode)
ALBs and Classic ELBs don't fully support HTTP2/gRPC, which is used by the `argocd` CLI.

View File

@@ -28,7 +28,7 @@ Breaking down the permissions definition differs slightly between applications a
### RBAC Resources and Actions
Resources: `clusters`, `projects`, `applications`, `repositories`, `certificates`
Resources: `clusters`, `projects`, `applications`, `repositories`, `certificates`, `accounts`, `gpgkeys`
Actions: `get`, `create`, `update`, `delete`, `sync`, `override`, `action`

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